Files
raven-client/ayanova/src/views/ay-data-list-column-view.vue
2022-01-11 22:08:38 +00:00

477 lines
13 KiB
Vue

<template>
<v-row v-if="formState.ready">
<v-col>
<v-form ref="form" data-cy="dlcForm">
<v-row>
<gz-error :error-box-message="formState.errorBoxMessage"></gz-error>
<template v-for="(item, index) in editView">
<v-col :key="item.key" cols="12" sm="6" lg="4" xl="3" px-2>
<v-card
:elevation="item.affective ? 10 : 1"
:data-cy="'columncard:' + item.key"
>
<v-card-title>
<span
:class="{
'accent--text text-h5': item.affective
}"
>
{{ item.title }}
</span>
<div v-if="item.affective">
<v-icon color="accent">$ayiFilter</v-icon>
<v-icon color="accent">$ayiSort</v-icon>
</div>
</v-card-title>
<v-card-text>
<!-- INCLUDE CONTROL -->
<!-- {{ item }} -->
<v-switch
v-if="!item.rid"
:ref="item.key"
v-model="item.include"
:label="$ay.t('Include')"
@change="includeChanged(item)"
></v-switch>
<div v-if="item.rid" class="v-label mb-8 mt-6">
{{ $ay.t("Include") }}
</div>
<!-- RE-ORDER CONTROL -->
<div v-if="item.include" class="d-flex justify-space-between">
<v-btn large icon @click="move('start', index)"
><v-icon large data-cy="movestart"
>$ayiStepBackward</v-icon
></v-btn
>
<v-btn large icon @click="move('left', index)"
><v-icon large>$ayiBackward</v-icon></v-btn
>
<v-btn large icon @click="move('right', index)"
><v-icon large>$ayiForward</v-icon></v-btn
>
<v-btn large icon @click="move('end', index)"
><v-icon large>$ayiStepForward</v-icon></v-btn
>
</div>
</v-card-text>
</v-card>
</v-col>
</template>
</v-row>
</v-form>
</v-col>
</v-row>
</template>
<script>
//
// CUSTOMIZE A DATA GRID'S VISIBLE COLUMNS
//
const FORM_KEY = "ay-data-list-column-view";
const API_BASE_URL = "data-list-column-view/";
export default {
async beforeRouteLeave(to, from, next) {
if (!this.formState.dirty) {
next();
return;
}
if ((await window.$gz.dialog.confirmLeaveUnsaved()) === true) {
next();
} else {
next(false);
}
},
data() {
return {
obj: {
id: 0,
concurrency: 0,
userId: 0,
listKey: null,
columns: null,
sort: null
},
dataListKey: undefined,
hiddenAffectiveColumns: [],
fieldDefinitions: [],
editView: [],
formState: {
ready: false,
dirty: false,
valid: true,
readOnly: false,
loading: true,
errorBoxMessage: undefined,
appError: undefined,
serverError: {}
},
rights: window.$gz.role.fullRightsObject()
};
},
computed: {
canSave: function() {
return this.formState.valid && this.formState.dirty;
},
canDuplicate: function() {
return this.formState.valid && !this.formState.dirty;
}
},
watch: {
formState: {
handler: function(val) {
if (this.formState.loading) {
return;
}
const canSave = val.dirty && val.valid && !val.readOnly;
if (canSave) {
window.$gz.eventBus.$emit("menu-enable-item", FORM_KEY + ":save");
} else {
window.$gz.eventBus.$emit("menu-disable-item", FORM_KEY + ":save");
}
},
deep: true
}
},
beforeDestroy() {
window.$gz.eventBus.$off("menu-click", clickHandler);
},
async created() {
const vm = this;
try {
vm.dataListKey = this.$route.params.dataListKey;
if (this.$route.params.hiddenAffectiveColumns) {
vm.hiddenAffectiveColumns = this.$route.params.hiddenAffectiveColumns;
}
await initForm(vm);
vm.formState.ready = true;
window.$gz.eventBus.$on("menu-click", clickHandler);
generateMenu(vm);
//init disable save button so it can be enabled only on edit to show dirty form
window.$gz.eventBus.$emit("menu-disable-item", FORM_KEY + ":save");
vm.formState.loading = false;
} catch (err) {
vm.formState.ready = true;
window.$gz.errorHandler.handleFormError(err, vm);
}
},
methods: {
enumSelectionList: function(enumKey) {
return window.$gz.enums.getSelectionList(enumKey);
},
includeChanged: function(item) {
if (item.required && item.visible == false) {
item.required = false;
}
window.$gz.form.setFormState({
vm: this,
dirty: true
});
},
move: function(direction, index) {
const totalItems = this.editView.length;
let newIndex = 0;
//calculate new index
switch (direction) {
case "start":
newIndex = 0;
break;
case "left":
newIndex = index - 1;
if (newIndex < 0) {
newIndex = 0;
}
break;
case "right":
newIndex = index + 1;
if (newIndex > totalItems - 1) {
newIndex = totalItems - 1;
}
break;
case "end":
newIndex = totalItems - 1;
break;
}
this.editView.splice(newIndex, 0, this.editView.splice(index, 1)[0]);
window.$gz.form.setFormState({
vm: this,
dirty: true
});
},
form() {
return window.$gz.form;
},
fieldValueChanged(ref) {
if (!this.formState.loading && !this.formState.readOnly) {
window.$gz.form.fieldValueChanged(this, ref);
}
},
async submit() {
if (this.canSave) {
this.formState.loading = true;
const columnView = {
userId: this.$store.state.userId,
listKey: this.dataListKey,
columns: JSON.stringify(generateColumnViewFromEditView(this)),
sort: this.obj.sort //not set here, just keep existing one that was fetched when opened this form
};
window.$gz.form.deleteAllErrorBoxErrors(this);
try {
const res = await window.$gz.api.post(API_BASE_URL, columnView);
this.formState.loading = false;
if (res.error) {
this.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(this);
} else {
this.obj = res.data;
initWorkingView(this);
window.$gz.form.setFormState({
vm: this,
dirty: false,
valid: true
});
}
} catch (error) {
this.formState.loading = false;
window.$gz.errorHandler.handleFormError(error, this);
}
}
},
async reset() {
const vm = this;
try {
vm.formState.loading = true;
window.$gz.form.deleteAllErrorBoxErrors(vm);
const res = await window.$gz.api.remove(API_BASE_URL + vm.dataListKey);
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
//this "remove" route is a reset route and returns the object
vm.obj = res.data;
initWorkingView(vm);
window.$gz.form.setFormState({
vm: vm,
dirty: false,
valid: true
});
}
} catch (error) {
window.$gz.form.setFormState({
vm: vm,
loading: false
});
window.$gz.errorHandler.handleFormError(error, vm);
}
}
}
};
/////////////////////////////
//
//
function clickHandler(menuItem) {
if (!menuItem) {
return;
}
const m = window.$gz.menu.parseMenuItem(menuItem);
if (m.owner == FORM_KEY && !m.disabled) {
switch (m.key) {
case "save":
m.vm.submit();
break;
case "reset":
m.vm.reset();
break;
case "duplicate":
m.vm.duplicate();
break;
default:
window.$gz.eventBus.$emit(
"notify-warning",
FORM_KEY + "::context click: [" + m.key + "]"
);
}
}
}
//////////////////////
//
//
function generateMenu(vm) {
const menuOptions = {
isMain: false,
readOnly: vm.formState.readOnly,
icon: "$ayiColumns",
title: "Columns",
helpUrl: "ay-start-form-data-tables#column-selector",
formData: {
ayaType: window.$gz.type.FormCustom,
formCustomTemplateKey: undefined,
recordName: vm.obj.name
},
menuItems: []
};
menuOptions.menuItems.push({
title: "Save",
icon: "$ayiSave",
surface: true,
key: FORM_KEY + ":save",
vm: vm
});
menuOptions.menuItems.push({
title: "ResetToDefault",
icon: "$ayiUndo",
surface: false,
key: FORM_KEY + ":reset",
vm: vm
});
window.$gz.eventBus.$emit("menu-change", menuOptions);
}
/////////////////////////////////
//
//
async function initForm(vm) {
await fetchTranslatedText();
await populateFieldDefinitions(vm);
await fetchTranslatedFieldNames(vm);
await fetchColumnView(vm);
initWorkingView(vm);
}
//////////////////////////////////////////////////////////
//
// Ensures UI translated text is available
//
async function fetchTranslatedText() {
await window.$gz.translation.cacheTranslations([
"ResetToDefault",
"Columns",
"Include"
]);
}
////////////////////
//
async function populateFieldDefinitions(vm) {
const res = await window.$gz.api.get(
"data-list/listfields?DataListKey=" + vm.dataListKey
);
if (res.error) {
throw new Error(window.$gz.errorHandler.errorToString(res, vm));
} else {
vm.fieldDefinitions = res.data;
}
}
//////////////////////////////////////////////////////////
//
// Ensures column names are present in translation table
//
async function fetchTranslatedFieldNames(vm) {
const columnKeys = [];
for (let i = 0; i < vm.fieldDefinitions.length; i++) {
const cm = vm.fieldDefinitions[i];
if (!columnKeys.includes(cm.tKey)) {
columnKeys.push(cm.tKey);
}
//get section names too if present
if (cm.tKeySection != null && !columnKeys.includes(cm.tKeySection)) {
columnKeys.push(cm.tKeySection);
}
}
await window.$gz.translation.cacheTranslations(columnKeys);
}
/////////////////////////////////
//
//
async function fetchColumnView(vm) {
const res = await window.$gz.api.get(API_BASE_URL + vm.dataListKey);
if (res.error) {
throw new Error(window.$gz.errorHandler.errorToString(res, vm));
} else {
vm.obj = res.data;
}
}
////////////////////
//
function initWorkingView(vm) {
if (vm.fieldDefinitions == null) {
throw new Error(
"ay-data-list::initWorkingView - fieldDefinitions are not set"
);
}
const ret = [];
const columns = JSON.parse(vm.obj.columns);
//Pass 1, iterate the columns first
for (let i = 0; i < columns.length; i++) {
const fld = vm.fieldDefinitions.find(z => z.fieldKey == columns[i]);
//there can be a column definition that doesn't exist due to updates or misconfiguration or other issues so ignore it if that's the case
if (fld) {
const o = {
key: fld.fieldKey,
title: null,
include: true
};
if (fld.tKeySection != null) {
o.title = vm.$ay.t(fld.tKeySection) + "." + vm.$ay.t(fld.tKey);
} else {
o.title = vm.$ay.t(fld.tKey);
}
if (fld.isRowId) {
o.rid = true;
}
ret.push(o);
}
}
//Pass 2, remaining fields not already dealt with
for (let i = 0; i < vm.fieldDefinitions.length; i++) {
const fld = vm.fieldDefinitions[i];
if (null == ret.find(z => z.key == fld.fieldKey)) {
//nope, so add it
const o = {
key: fld.fieldKey,
title: null,
include: false
};
if (fld.tKeySection != null) {
o.title = vm.$ay.t(fld.tKeySection) + "." + vm.$ay.t(fld.tKey);
} else {
o.title = vm.$ay.t(fld.tKey);
}
if (fld.isRowId) {
o.rid = true;
o.include = true;
}
if (vm.hiddenAffectiveColumns.includes(fld.fieldKey)) {
o.affective = true;
}
ret.push(o);
}
}
vm.editView = ret;
}
//////////////////////////////////////////////////////////
//
// Convert editedList view to real list view and return
//
function generateColumnViewFromEditView(vm) {
const ret = [];
for (let i = 0; i < vm.editView.length; i++) {
const ev = vm.editView[i];
if (!ev.include) {
continue;
}
ret.push(ev.key);
}
return ret;
}
</script>