Files
raven-client/ayanova/src/views/adm-translation.vue
John Cardinal 223a9e4060 Removed clearable from most forms, it's too "busy" and fucks up the look of the form for not much benefit
maybe in future can look at only showing on mobile or something or when the field is active?
2020-11-11 20:03:53 +00:00

706 lines
20 KiB
Vue

<template>
<!-- <v-container fluid> -->
<div>
<gz-report-selector ref="reportSelector"></gz-report-selector>
<!-- {{ formState }} -->
<v-row justify="center">
<v-dialog v-model="replaceDialog" persistent max-width="600px">
<v-card>
<v-card-title>
<span class="headline">{{ $ay.t("FindAndReplace") }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field
v-model="find"
:label="$ay.t('Find')"
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
v-model="replace"
:label="$ay.t('Replace')"
required
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="replaceDialog = false">{{
$ay.t("Cancel")
}}</v-btn>
<v-btn color="blue darken-1" text @click="doReplace()">{{
$ay.t("OK")
}}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
<v-row v-if="formState.ready">
<v-col>
<v-form ref="form">
<v-row>
<gz-error :errorBoxMessage="formState.errorBoxMessage"></gz-error>
<v-col cols="12" v-if="obj.stock && rights.change">
<span class="text-h4 warning--text mr-6">{{
$ay.t("ReadOnly")
}}</span>
<v-btn @click="duplicate()" :loading="duplicating">
<v-icon left>$ayiClone</v-icon> {{ $ay.t("Duplicate") }}
</v-btn>
</v-col>
<v-col cols="12" sm="6" lg="4" xl="3">
<v-text-field
v-model="obj.name"
:readonly="formState.readOnly"
:disabled="formState.readOnly"
:label="$ay.t('Name')"
:rules="[form().required(this, 'name')]"
:error-messages="form().serverErrors(this, 'name')"
ref="name"
data-cy="name"
@input="fieldValueChanged('name')"
></v-text-field>
</v-col>
<v-col cols="12" sm="6" lg="4" xl="3">
<v-checkbox
v-model="obj.cjkIndex"
:readonly="formState.readOnly"
:disabled="formState.readOnly"
:label="$ay.t('GlobalCJKIndex')"
:hint="$ay.t('GlobalCJKIndexDescription')"
:persistent-hint="true"
ref="cjkIndex"
:error-messages="form().serverErrors(this, 'cjkIndex')"
@change="fieldValueChanged('cjkIndex')"
></v-checkbox>
</v-col>
<!-- ----------------------- -->
<v-col cols="12">
<v-card>
<v-card-title>
<v-text-field
v-model="search"
append-icon="$ayiSearch"
:label="$ay.t('Search')"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:headers="[
{
text: $ay.t('TranslationKey'),
align: 'start',
value: 'key'
},
{ text: $ay.t('TranslationDisplayText'), value: 'display' }
]"
:items="obj.translationItems"
:footer-props="{
itemsPerPageOptions: [5, 10, 25, 50, 100],
itemsPerPageText: $ay.t('RowsPerPage'),
pageText: $ay.t('PageOfPageText')
}"
:header-props="{ sortByText: $ay.t('Sort') }"
:search="search"
must-sort
>
<template
v-slot:[`item.display`]="props"
v-if="!formState.readOnly"
>
<v-edit-dialog
large
:return-value.sync="props.item.display"
:cancel-text="$ay.t('Cancel')"
:save-text="$ay.t('OK')"
@save="saveItem(props.item)"
>
{{ props.item.display }}
<template v-slot:input>
<v-text-field
v-model="props.item.display"
label="Edit"
single-line
></v-text-field>
</template>
</v-edit-dialog>
</template>
</v-data-table>
</v-card>
</v-col>
<!-- ------------------- -->
</v-row>
</v-form>
</v-col>
</v-row>
<template v-if="!formState.ready">
<v-progress-circular
indeterminate
color="primary"
:size="60"
></v-progress-circular>
</template>
</div>
<!-- </v-container> -->
</template>
<script>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* Xeslint-disable */
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
const FORM_KEY = "translation-edit";
const API_BASE_URL = "translation/";
const FORM_CUSTOM_TEMPLATE_KEY = "Translation"; //<-- Should always be CoreBizObject AyaType name here where possible
export default {
async created() {
//created is called when the route is updated to show a new record even though we don't need to re-init again
let vm = this;
try {
await initForm(vm);
vm.rights = window.$gz.role.getRights(window.$gz.type.Translation);
vm.formState.readOnly = !vm.rights.change;
window.$gz.eventBus.$on("menu-click", clickHandler);
//id 0 means create a new record don't load one
if (vm.$route.params.recordid != 0) {
//is there already an obj from a prior operation?
if (this.$route.params.obj) {
//yes, no need to fetch it
this.obj = this.$route.params.obj;
vm.formState.loading = false; //here we handle it immediately
} else {
await vm.getDataFromApi(vm.$route.params.recordid); //let getdata handle loading
}
} else {
vm.formState.loading = false; //here we handle it immediately
}
//set initial form status
window.$gz.form.setFormState({
vm: vm,
dirty: false,
valid: true
});
generateMenu(vm);
} catch (error) {
window.$gz.errorHandler.handleFormError(error, vm);
} finally {
vm.formState.ready = true;
}
},
async beforeRouteLeave(to, from, next) {
if (!this.formState.dirty || JUST_DELETED) {
next();
return;
}
if ((await window.$gz.dialog.confirmLeaveUnsaved()) === true) {
next();
} else {
next(false);
}
},
beforeDestroy() {
window.$gz.eventBus.$off("menu-click", clickHandler);
},
components: {},
data() {
return {
formCustomTemplateKey: FORM_CUSTOM_TEMPLATE_KEY,
search: "",
find: "",
replace: "",
replaceDialog: false,
editingActiveTranslation: false,
duplicating: false,
obj: {},
formState: {
ready: false,
dirty: false,
valid: true,
readOnly: false,
loading: true,
errorBoxMessage: null,
appError: null,
serverError: {}
},
rights: window.$gz.role.defaultRightsObject(),
ayaType: window.$gz.type.Translation
};
},
//WATCHERS
watch: {
formState: {
handler: function(val) {
if (this.formState.loading) {
return;
}
//enable / disable save button
if (val.dirty && val.valid && !val.readOnly) {
window.$gz.eventBus.$emit("menu-enable-item", FORM_KEY + ":save");
} else {
window.$gz.eventBus.$emit("menu-disable-item", FORM_KEY + ":save");
}
//enable / disable duplicate / new button
if (!val.dirty && val.valid) {
window.$gz.eventBus.$emit(
"menu-enable-item",
FORM_KEY + ":duplicate"
);
window.$gz.eventBus.$emit("menu-enable-item", FORM_KEY + ":new");
} else {
window.$gz.eventBus.$emit(
"menu-disable-item",
FORM_KEY + ":duplicate"
);
window.$gz.eventBus.$emit("menu-disable-item", FORM_KEY + ":new");
}
},
deep: true
}
},
computed: {
canSave: function() {
return this.formState.valid && this.formState.dirty;
}
},
methods: {
doReplace() {
let changesMade = false;
if (this.find && this.replace) {
for (let i = 0; i < this.obj.translationItems.length; i++) {
if (
!changesMade &&
this.obj.translationItems[i].display.includes(this.find)
) {
changesMade = true;
}
this.obj.translationItems[i].display = this.obj.translationItems[
i
].display
.split(this.find)
.join(this.replace);
}
if (changesMade == true) {
window.$gz.form.setFormState({
vm: this,
dirty: true
});
}
}
this.replaceDialog = false;
},
saveItem(updatedItem) {
//just called to flag as dirty
window.$gz.form.setFormState({
vm: this,
dirty: true
});
},
filteredItems() {
return this.obj.translationItems;
},
canDuplicate: function() {
return this.formState.valid && !this.formState.dirty && vm.rights.change;
},
ayaTypes: function() {
return window.$gz.type;
},
form() {
return window.$gz.form;
},
fieldValueChanged(ref) {
if (
this.formState.ready &&
!this.formState.loading &&
!this.formState.readOnly
) {
window.$gz.form.fieldValueChanged(this, ref);
}
},
async getDataFromApi(recordId) {
let vm = this;
vm.formState.loading = true;
if (!recordId) {
throw new Error(FORM_KEY + "::getDataFromApi -> Missing recordID!");
}
let url = API_BASE_URL + recordId;
try {
window.$gz.form.deleteAllErrorBoxErrors(vm);
let res = await window.$gz.api.get(url);
if (res.error) {
//Not found?
if (res.error.code == "2010") {
window.$gz.form.handleObjectNotFound(vm);
}
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
vm.formState.loading = false;
} else {
vm.obj = res.data;
//modify the menu as necessary
generateMenu(vm);
//Update the form status
window.$gz.form.setFormState({
vm: vm,
dirty: false,
valid: true,
loading: false,
readOnly: !vm.rights.change || res.data.stock == true
});
}
} catch (error) {
window.$gz.errorHandler.handleFormError(error, vm);
vm.formState.loading = false;
}
},
async submit() {
let vm = this;
if (vm.canSave == false) {
return;
}
try {
vm.formState.loading = true;
let url = API_BASE_URL;
//clear any errors vm might be around from previous submit
window.$gz.form.deleteAllErrorBoxErrors(vm);
let res = await window.$gz.api.upsert(url, vm.obj);
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
//Logic for detecting if a post or put: if id then it was a post, if no id then it was a put
if (res.data.id) {
//POST - whole new object returned
vm.obj = res.data;
//Change URL to new record
//NOTE: will not cause a page re-render, almost nothing does unless forced with a KEY property or using router.GO()
this.$router.push({
name: "adm-translation",
params: {
recordid: res.data.id,
obj: res.data //Pass data object to new form
}
});
} else {
//PUT - only concurrency token is returned (**warning, if server changes object other fields then this needs to act more like POST above but is more efficient this way**)
//Handle "put" of an existing record (UPDATE)
vm.obj.concurrency = res.data.concurrency;
//Update local copy of translations if that's the same one in use
if (vm.editingActiveTranslation) {
await window.$gz.translation.updateCache(vm.obj);
}
}
//Update the form status
window.$gz.form.setFormState({
vm: vm,
dirty: false,
valid: true
});
}
} catch (ex) {
window.$gz.errorHandler.handleFormError(ex, vm);
} finally {
vm.formState.loading = false;
}
},
async remove() {
let vm = this;
try {
let dialogResult = await window.$gz.dialog.confirmDelete();
if (dialogResult != true) {
return;
}
//do the delete
vm.formState.loading = true;
//No need to delete a new record, just abandon it...
if (vm.$route.params.recordid == 0) {
//this should not get offered for delete but to be safe and clear just in case:
JUST_DELETED = true;
// navigate backwards
vm.$router.go(-1);
} else {
let url = API_BASE_URL + vm.$route.params.recordid;
window.$gz.form.deleteAllErrorBoxErrors(vm);
let res = await window.$gz.api.remove(url);
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
//workaround to prevent warning about leaving dirty record
//For some reason I couldn't just reset isdirty in formstate
JUST_DELETED = true;
// navigate backwards
vm.$router.go(-1);
}
}
} catch (ex) {
window.$gz.errorHandler.handleFormError(ex, vm);
} finally {
vm.formState.loading = false;
}
},
async duplicate() {
let vm = this;
if (!vm.canDuplicate || vm.$route.params.recordid == 0) {
return;
}
vm.formState.loading = true;
let url = API_BASE_URL + "duplicate/" + vm.$route.params.recordid;
try {
window.$gz.form.deleteAllErrorBoxErrors(vm);
vm.duplicating = true;
let res = await window.$gz.api.upsert(url);
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
//Navigate to new record
this.$router.push({
name: "adm-translation",
params: {
recordid: res.data.id,
obj: res.data // pass data object to new form
}
});
}
} catch (ex) {
window.$gz.errorHandler.handleFormError(ex, vm);
} finally {
vm.formState.loading = false;
vm.duplicating = false;
}
}
}
};
/////////////////////////////
//
//
async function clickHandler(menuItem) {
if (!menuItem) {
return;
}
let m = window.$gz.menu.parseMenuItem(menuItem);
if (m.owner == FORM_KEY && !m.disabled) {
switch (m.key) {
case "save":
m.vm.submit();
break;
case "replace":
m.vm.replaceDialog = true;
break;
case "export":
//ignore download link let it go
break;
case "delete":
m.vm.remove();
break;
case "new":
m.vm.$router.push({
name: "adm-translation",
params: { recordid: 0, new: true }
});
break;
case "duplicate":
m.vm.duplicate();
break;
case "report":
if (m.id != null) {
//last report selected
m.vm.$router.push({
name: "ay-report",
params: { recordid: m.id, ayatype: window.$gz.type.Translation }
});
} else {
//general report selector chosen
let res = await m.vm.$refs.reportSelector.open();
//if null for no selection
//just bail out
if (res == null) {
return;
}
//persist last report selected
window.$gz.form.setLastReport(FORM_KEY, res);
//Now open the report viewer...
m.vm.$router.push({
name: "ay-report",
params: { recordid: res.id, ayatype: window.$gz.type.Translation }
});
}
break;
default:
window.$gz.eventBus.$emit(
"notify-warning",
FORM_KEY + "::context click: [" + m.key + "]"
);
}
}
}
//////////////////////
//
//
function generateMenu(vm) {
let menuOptions = {
isMain: false,
icon: "$ayiLanguage",
title: "Translation",
helpUrl: "form-adm-translation",
formData: {
ayaType: window.$gz.type.Translation,
recordId: vm.$route.params.recordid,
formCustomTemplateKey: FORM_CUSTOM_TEMPLATE_KEY
},
menuItems: []
};
if (vm.rights.change && vm.obj.stock != true) {
menuOptions.menuItems.push({
title: "Save",
icon: "$ayiSave",
surface: true,
key: FORM_KEY + ":save",
vm: vm
});
}
if (
vm.rights.delete &&
vm.$route.params.recordid != 0 &&
vm.obj.stock != true
) {
menuOptions.menuItems.push({
title: "Delete",
icon: "$ayiTrashAlt",
surface: false,
key: FORM_KEY + ":delete",
vm: vm
});
}
//STUB REPORTS
//Report not Print, print is a further option
menuOptions.menuItems.push({
title: "Report",
icon: "$ayiFileAlt",
key: FORM_KEY + ":report",
vm: vm
});
//get last report selected
let lastReport = window.$gz.form.getLastReport(FORM_KEY);
if (lastReport != null) {
menuOptions.menuItems.push({
title: lastReport.name,
icon: "$ayiFileAlt",
key: FORM_KEY + ":report:" + lastReport.id,
vm: vm
});
}
if (vm.rights.change && vm.$route.params.recordid != 0) {
menuOptions.menuItems.push({
title: "Duplicate",
icon: "$ayiClone",
key: FORM_KEY + ":duplicate",
vm: vm
});
}
if (vm.rights.change && vm.obj.stock != true) {
menuOptions.menuItems.push({
title: "FindAndReplace",
icon: null,
key: FORM_KEY + ":replace",
vm: vm
});
}
//EXPORT
if (vm.$route.params.recordid != 0) {
let href = window.$gz.api.genericDownloadUrl(
"translation/download/" + vm.$route.params.recordid
);
menuOptions.menuItems.push({
title: "Export",
icon: "$ayiFileDownload",
href: href,
target: "_blank",
key: FORM_KEY + ":export",
vm: vm
});
}
window.$gz.eventBus.$emit("menu-change", menuOptions);
}
let JUST_DELETED = false;
/////////////////////////////////
//
//
async function initForm(vm) {
await fetchTranslatedText(vm);
//await window.$gz.formCustomTemplate.get(FORM_CUSTOM_TEMPLATE_KEY);
await setEditingActiveTranslation(vm);
}
//////////////////////////////////////////////////////////
//
// Ensures UI translated text is available
//
async function fetchTranslatedText(vm) {
await window.$gz.translation.cacheTranslations([
"Translation",
"Name",
"TranslationKey",
"TranslationDisplayText",
"FindAndReplace",
"Find",
"Replace",
"GlobalCJKIndex",
"GlobalCJKIndexDescription"
]);
}
//////////////////////////////////////////////////////////
//
//
async function setEditingActiveTranslation(vm) {
if (vm.$route.params.recordid != 0) {
let res = await window.$gz.api.get("user-option/" + vm.$store.state.userId);
vm.editingActiveTranslation =
res.data.translationId == vm.$route.params.recordid;
}
}
</script>