989 lines
33 KiB
Vue
989 lines
33 KiB
Vue
<template>
|
|
<div>
|
|
<gz-error :errorBoxMessage="formState.errorBoxMessage"></gz-error>
|
|
<v-card>
|
|
<v-card-title>
|
|
<v-select
|
|
v-model="listViewId"
|
|
:items="selectLists.listViews"
|
|
item-text="name"
|
|
item-value="id"
|
|
:label="$ay.t('DataListView')"
|
|
@input="listViewChanged"
|
|
:data-cy="!!$ay.dev ? 'selectlistview' : false"
|
|
>
|
|
</v-select>
|
|
<v-spacer></v-spacer>
|
|
<div>
|
|
<v-btn @click="refresh">
|
|
<v-icon :data-cy="!!$ay.dev ? 'refresh' : false">fa-sync</v-icon>
|
|
</v-btn>
|
|
|
|
<v-btn class="ml-12" @click="editListView">
|
|
<v-icon :data-cy="!!$ay.dev ? 'filter' : false">fa-filter</v-icon>
|
|
</v-btn>
|
|
</div>
|
|
</v-card-title>
|
|
<div
|
|
class="headline ml-3 accent--text"
|
|
v-if="!loading && records.length < 1"
|
|
>
|
|
{{ $ay.t("NoData") }}
|
|
</div>
|
|
<!-- WIDE TABLE VIEW -->
|
|
<template v-if="$vuetify.breakpoint.smAndUp">
|
|
<v-data-table
|
|
:headers="headers"
|
|
:items="records"
|
|
v-model="selected"
|
|
:options.sync="dataTablePagingOptions"
|
|
:server-items-length="totalRecords"
|
|
:loading="loading"
|
|
:disable-sort="true"
|
|
:show-select="showSelect"
|
|
:single-select="singleSelect"
|
|
:footer-props="{
|
|
showCurrentPage: true,
|
|
showFirstLastPage: true,
|
|
itemsPerPageOptions: rowsPerPageItems,
|
|
itemsPerPageText: $ay.t('RowsPerPage'),
|
|
pageText: $ay.t('PageOfPageText')
|
|
}"
|
|
:loading-text="$ay.t('Loading')"
|
|
:no-data-text="$ay.t('NoData')"
|
|
class="elevation-1"
|
|
:data-cy="!!$ay.dev ? 'datatable-wide' : false"
|
|
>
|
|
<template v-slot:body="{ items }">
|
|
<tbody>
|
|
<tr v-for="item in items" :key="item.id">
|
|
<template v-if="showSelect">
|
|
<td>
|
|
<v-checkbox
|
|
v-model="selected"
|
|
:value="item"
|
|
hide-details
|
|
></v-checkbox>
|
|
</td>
|
|
</template>
|
|
<td v-for="c in item.columns" :key="c.key">
|
|
<template v-if="c.t == 1">
|
|
<!-- DATETIME -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 2">
|
|
<!-- DATE -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 3">
|
|
<!-- TIME -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 4">
|
|
<!-- TEXT (also maybe openable)-->
|
|
<template v-if="c.i">
|
|
<!-- openable object with an ID -->
|
|
<div
|
|
class="subtitle-1"
|
|
@click="gridCellButtonClick(c.key, c.i)"
|
|
>
|
|
<a href="javascript:"> {{ c.v }}</a>
|
|
</div>
|
|
<!-- <v-btn
|
|
depressed
|
|
small
|
|
@click="gridCellButtonClick(c.key, c.i)"
|
|
>{{ c.v }}</v-btn> -->
|
|
</template>
|
|
<template v-else>
|
|
{{ c.v }}
|
|
</template>
|
|
</template>
|
|
<template v-else-if="c.t == 5">
|
|
<!-- INTEGER -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 6">
|
|
<!-- BOOL -->
|
|
<div class="text-center">
|
|
<v-icon v-if="c.v === false" small>far fa-square</v-icon>
|
|
<v-icon v-else-if="c.v === true" small
|
|
>far fa-check-square</v-icon
|
|
>
|
|
<v-icon v-else small>far fa-minus-square</v-icon>
|
|
</div>
|
|
</template>
|
|
<template v-else-if="c.t == 7">
|
|
<!-- DECIMAL -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 8">
|
|
<!-- CURRENCY -->
|
|
<div class="text-right">{{ c.v }}</div>
|
|
</template>
|
|
<template v-else-if="c.t == 9">
|
|
<!-- TAGS -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 10">
|
|
<!-- ENUM (translated to text on getdata) -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 11">
|
|
<!-- EMAIL -->
|
|
<a :href="'mailto:' + c.v">{{ c.v }}</a>
|
|
</template>
|
|
<template v-else-if="c.t == 12">
|
|
<!-- URL / HTTP -->
|
|
<!-- Expects full url with protocol etc in c.v so might need to add to record builder -->
|
|
<a :href="c.v" target="_blank">{{ c.v }}</a>
|
|
</template>
|
|
<template v-else>
|
|
<!-- UNKNOWN -->
|
|
{{ c.v }}
|
|
</template>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</template>
|
|
</v-data-table>
|
|
</template>
|
|
<!-- MOBILE TABLE VIEW -->
|
|
<template v-else>
|
|
<v-data-iterator
|
|
:headers="headers"
|
|
:items="records"
|
|
v-model="selected"
|
|
:options.sync="dataTablePagingOptions"
|
|
:server-items-length="totalRecords"
|
|
:loading="loading"
|
|
:disable-sort="true"
|
|
:show-select="showSelect"
|
|
:single-select="singleSelect"
|
|
:footer-props="{
|
|
showCurrentPage: true,
|
|
showFirstLastPage: true,
|
|
itemsPerPageOptions: rowsPerPageItems,
|
|
itemsPerPageText: $ay.t('RowsPerPage'),
|
|
pageText: $ay.t('PageOfPageText')
|
|
}"
|
|
:loading-text="$ay.t('Loading')"
|
|
class="elevation-1"
|
|
:data-cy="!!$ay.dev ? 'datatable-mobile' : false"
|
|
>
|
|
<template v-slot:header="props">
|
|
<!-- Mimic the full width data table select all toggle :no-data-text="$ay.t('NoData')"-->
|
|
<div
|
|
id="divSelectAll"
|
|
@click="props.toggleSelectAll(!props.everyItem)"
|
|
class="pl-2 pt-2"
|
|
>
|
|
<v-icon v-if="!props.someItems" large>far fa-square</v-icon>
|
|
<v-icon v-if="props.someItems && !props.everyItem" large
|
|
>far fa-minus-square</v-icon
|
|
>
|
|
<v-icon v-if="props.everyItem" large>far fa-check-square</v-icon>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-slot:default="{ items }">
|
|
<v-row>
|
|
<v-col
|
|
v-for="item in items"
|
|
:key="item.id"
|
|
cols="12"
|
|
sm="6"
|
|
md="4"
|
|
lg="3"
|
|
>
|
|
<v-card elevation="4" tile>
|
|
<template v-if="showSelect">
|
|
<v-card-title class="subheading font-weight-bold">
|
|
<v-checkbox
|
|
v-model="selected"
|
|
:value="item"
|
|
hide-details
|
|
></v-checkbox>
|
|
</v-card-title>
|
|
<v-divider></v-divider>
|
|
</template>
|
|
<v-list dense>
|
|
<v-list-item
|
|
two-line
|
|
v-for="c in item.columns"
|
|
:key="c.key"
|
|
>
|
|
<v-list-item-content>
|
|
<v-list-item-title>
|
|
{{ getHeaderText(c.key) }}</v-list-item-title
|
|
>
|
|
<v-list-item-subtitle>
|
|
<template v-if="c.t == 1">
|
|
<!-- DATETIME -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 2">
|
|
<!-- DATE -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 3">
|
|
<!-- TIME -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 4">
|
|
<!-- TEXT (also maybe openable)-->
|
|
<template v-if="c.i">
|
|
<!-- openable object with an ID -->
|
|
<div
|
|
class="subtitle-1"
|
|
@click="gridCellButtonClick(c.key, c.i)"
|
|
>
|
|
<a href="javascript:"> {{ c.v }}</a>
|
|
</div>
|
|
<!-- <span
|
|
@click="gridCellButtonClick(c.key, c.i)"
|
|
class="primary--text subtitle-1 font-weight-bold"
|
|
style="cursor:pointer"
|
|
>{{ c.v }}
|
|
</span> -->
|
|
</template>
|
|
<template v-else>
|
|
{{ c.v }}
|
|
</template>
|
|
</template>
|
|
<template v-else-if="c.t == 5">
|
|
<!-- INTEGER -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 6">
|
|
<!-- BOOL -->
|
|
<div>
|
|
<v-icon v-if="c.v === false" small
|
|
>far fa-square</v-icon
|
|
>
|
|
<v-icon v-else-if="c.v === true" small
|
|
>far fa-check-square</v-icon
|
|
>
|
|
<v-icon v-else small>far fa-minus-square</v-icon>
|
|
</div>
|
|
</template>
|
|
<template v-else-if="c.t == 7">
|
|
<!-- DECIMAL -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 8">
|
|
<!-- CURRENCY -->
|
|
<div>{{ c.v }}</div>
|
|
</template>
|
|
<template v-else-if="c.t == 9">
|
|
<!-- TAGS -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 10">
|
|
<!-- ENUM (translated to text on getdata) -->
|
|
{{ c.v }}
|
|
</template>
|
|
<template v-else-if="c.t == 11">
|
|
<!-- EMAIL -->
|
|
<a :href="'mailto:' + c.v">{{ c.v }}</a>
|
|
</template>
|
|
<template v-else-if="c.t == 12">
|
|
<!-- URL / HTTP -->
|
|
<!-- Expects full url with protocol etc in c.v so might need to add to record builder -->
|
|
<a :href="c.v" target="_blank">{{ c.v }}</a>
|
|
</template>
|
|
<template v-else>
|
|
<!-- UNKNOWN -->
|
|
{{ c.v }}
|
|
</template>
|
|
</v-list-item-subtitle>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
</v-list>
|
|
</v-card>
|
|
</v-col>
|
|
</v-row>
|
|
</template>
|
|
</v-data-iterator>
|
|
</template>
|
|
</v-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/* Xeslint-disable */
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
import relativeDatefilterCalculator from "../api/relative-date-filter-calculator.js";
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
loading: true,
|
|
dataTablePagingOptions: {},
|
|
lastDataTablePagingOptions: {},
|
|
listViewId: 0,
|
|
listView: undefined,
|
|
selectLists: {
|
|
listViews: []
|
|
},
|
|
headers: [],
|
|
serverColumns: [],
|
|
totalRecords: 0,
|
|
records: [],
|
|
rowsPerPageItems: [5, 10, 25, 50, 100],
|
|
selected: [],
|
|
formState: {
|
|
ready: false,
|
|
dirty: false,
|
|
valid: true,
|
|
readOnly: false,
|
|
loading: true,
|
|
errorBoxMessage: undefined,
|
|
appError: undefined,
|
|
serverError: {}
|
|
}
|
|
};
|
|
},
|
|
props: {
|
|
apiBaseUrl: {
|
|
type: String,
|
|
default: "data-list"
|
|
},
|
|
formKey: String,
|
|
dataListKey: String,
|
|
showSelect: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
singleSelect: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
},
|
|
watch: {
|
|
dataTablePagingOptions: {
|
|
handler() {
|
|
//{ "page": 1, "itemsPerPage": 10, "sortBy": [], "sortDesc": [], "groupBy": [], "groupDesc": [], "mustSort": false, "multiSort": false }
|
|
//this code works around some weird bug that causes visible items to be selected in grid (only, not in actual selected array, just a visual thing)
|
|
// when breakpoint is switched between wide and narrow either way. No idea why it happens but this fixes that issue and also ensures that there are no
|
|
//spurious fetches happening just because the view has changed
|
|
if (
|
|
this.lastDataTablePagingOptions.page ==
|
|
this.dataTablePagingOptions.page &&
|
|
this.lastDataTablePagingOptions.itemsPerPage ==
|
|
this.dataTablePagingOptions.itemsPerPage
|
|
) {
|
|
//no effective change, return
|
|
return;
|
|
}
|
|
//has changed something important so refetch and put a pin in last paging settings for next time
|
|
this.getDataFromApi();
|
|
this.lastDataTablePagingOptions = this.dataTablePagingOptions;
|
|
},
|
|
deep: true
|
|
},
|
|
selected: function(newValue, oldValue) {
|
|
if (!newValue) {
|
|
return;
|
|
}
|
|
if (newValue.length != oldValue.length) {
|
|
this.handleSelectChange();
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
//Used by narrow view to get the "header" text for a column based on the column key
|
|
getHeaderText(key) {
|
|
//key format is row-column e.g."500-2"
|
|
let columnIndex = key.split("-")[1];
|
|
let header = this.headers[columnIndex];
|
|
if (header && header.text) {
|
|
return header.text;
|
|
}
|
|
return "";
|
|
},
|
|
refresh() {
|
|
this.getDataFromApi();
|
|
},
|
|
handleSelectChange() {
|
|
//due to making own template for items need to handle singleselect which only affects if select all checkbox at top is visible when making own item template
|
|
if (this.singleSelect) {
|
|
this.selected.splice(0, this.selected.length - 1);
|
|
}
|
|
//emit event to parent form of selected rows
|
|
//this.$emit("update:selected", this.selected);
|
|
//Note vm this bubbles up all the columns of all the selected rows
|
|
|
|
//so, to be more efficient for now will just send the ID's until I see a need for other shit
|
|
this.$emit("input", window.$gz._.map(this.selected, "id"));
|
|
},
|
|
editListView() {
|
|
this.$router.push({
|
|
name: "ay-data-list-view",
|
|
params: {
|
|
dataListKey: this.dataListKey,
|
|
formKey: this.formKey,
|
|
listViewId: this.listViewId
|
|
}
|
|
});
|
|
},
|
|
|
|
listViewChanged: function() {
|
|
let vm = this;
|
|
|
|
//If listview had changed it can only have changed *away* from the unsaved filter item if it's present so just remove the unsaved filter item if it exists
|
|
|
|
//DANGER DANGER WARNING: if using lodash to remove item it might mess with vue reactivity
|
|
//this example does remove from the array and does update a plain mustache rendition of it on the page but doesn't update the select itself
|
|
//whereas using the native javascript array splice function *does* update the select because vue wraps splice and other native methods specifically
|
|
//so it can properly update the dom
|
|
// window.$gz._.remove(vm.selectLists.listViews, function(n) {
|
|
// return n.id == -1;
|
|
// });
|
|
|
|
for (let i = vm.selectLists.listViews.length - 1; i >= 0; i--) {
|
|
if (vm.selectLists.listViews[i].id === -1) {
|
|
vm.selectLists.listViews.splice(i, 1);
|
|
}
|
|
}
|
|
|
|
let ShouldGetData = vm.dataTablePagingOptions.page == 1;
|
|
|
|
if (vm.listViewId == 0) {
|
|
//default view, no saved, no cached
|
|
vm.listView = undefined;
|
|
|
|
saveFormSettings(vm);
|
|
if (ShouldGetData) {
|
|
vm.getDataFromApi();
|
|
} else {
|
|
vm.dataTablePagingOptions.page = 1;
|
|
}
|
|
//
|
|
} else if (vm.listViewId > 0) {
|
|
(async function() {
|
|
await fetchListView(vm);
|
|
|
|
saveFormSettings(vm);
|
|
if (ShouldGetData) {
|
|
vm.getDataFromApi();
|
|
} else {
|
|
vm.dataTablePagingOptions.page = 1;
|
|
}
|
|
})();
|
|
}
|
|
},
|
|
gridCellButtonClick(key, i) {
|
|
//translate key to actual object type from header data
|
|
//key format is row-column e.g."500-2"
|
|
//get the datatype of the column which matches the server columns array index
|
|
let typeToOpen = this.serverColumns[key.split("-")[1]].ay;
|
|
|
|
//i is the actual AyaNova index of vm record so we have all we need to open vm object
|
|
window.$gz.eventBus.$emit("openobject", { type: typeToOpen, id: i });
|
|
},
|
|
getDataFromApi() {
|
|
let vm = this;
|
|
if (vm.loading) {
|
|
return;
|
|
}
|
|
|
|
//start with defaults
|
|
let listOptions = {
|
|
DataListKey: vm.dataListKey,
|
|
Limit: 5,
|
|
Offset: 0
|
|
};
|
|
//calculate paging based on settings
|
|
const { page, itemsPerPage } = vm.dataTablePagingOptions;
|
|
if (itemsPerPage && itemsPerPage > 0) {
|
|
listOptions.Offset = (page - 1) * itemsPerPage;
|
|
listOptions.Limit = itemsPerPage;
|
|
}
|
|
|
|
vm.loading = true;
|
|
//Weird bug that causes grid to show all items selected after async method runs below to fetch data
|
|
//this puts a pin in it then resets it after the fetch is completed
|
|
let preSelected = [...vm.selected];
|
|
|
|
//untokenize ListView date token criteria (if there are any)
|
|
let untokenizedListView = untokenizeListView(vm.listView);
|
|
|
|
window.$gz.api
|
|
.upsert(vm.apiBaseUrl, {
|
|
offset: listOptions.Offset,
|
|
limit: listOptions.Limit,
|
|
dataListKey: vm.dataListKey,
|
|
listView: untokenizedListView
|
|
})
|
|
.then(res => {
|
|
if (res.error) {
|
|
throw res.error;
|
|
} else {
|
|
//NOTE: This is how to call an async function and await it from sync code
|
|
(async function() {
|
|
//Save a copy of the server columns data for handling button clicks etc later
|
|
vm.serverColumns = res.columns;
|
|
//Make sure the translation keys are fetched
|
|
await fetchTranslatedHeaderNames(res.columns); //Note can use await here because it's wrapped inside an async function call, it will wait then resume next stuff below
|
|
await fetchEnums(res.columns);
|
|
|
|
//build vm.headers here
|
|
vm.headers = buildHeaders(res.columns);
|
|
//Post process data here and then set vm.records
|
|
vm.records = buildRecords(res.data, res.columns);
|
|
|
|
vm.totalRecords = res.totalRecordCount;
|
|
//Put back selected items
|
|
vm.selected = [...preSelected];
|
|
|
|
//persist the paging options and listview stuff so user sees same page and list on refresh or leave and return scenario
|
|
saveFormSettings(vm);
|
|
//////////
|
|
})();
|
|
}
|
|
})
|
|
.catch(err => {
|
|
window.$gz.errorHandler.handleFormError(err, vm);
|
|
})
|
|
.finally(function() {
|
|
vm.loading = false;
|
|
vm.formState.ready = true;
|
|
});
|
|
}
|
|
},
|
|
created() {
|
|
//get pick lists
|
|
let vm = this;
|
|
|
|
initForm(vm).then(() => {
|
|
//rehydrate last form settings
|
|
//loadFormSettings(vm);
|
|
vm.loading = false;
|
|
vm.getDataFromApi();
|
|
});
|
|
}
|
|
};
|
|
|
|
//Called by getDataFromApi on retrieval of list with columnData
|
|
function buildHeaders(columnData) {
|
|
//iterate columns, build headers and return
|
|
if (!columnData) {
|
|
return [];
|
|
}
|
|
let ret = [];
|
|
//iterate the columns
|
|
for (let i = 0; i < columnData.length; i++) {
|
|
let cm = columnData[i];
|
|
let h = {};
|
|
h.text = window.$gz.translation.get(cm.cm);
|
|
h.value = "columns.c" + i.toString(); //+".v";
|
|
ret.push(h);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//Called by getDataFromApi on retrieval of list with columnData
|
|
function buildRecords(listData, columndefinitions) {
|
|
//iterate data, build each object keyed with index name and display set to correct translated filter and then return
|
|
|
|
let ret = [];
|
|
if (!listData) {
|
|
return ret;
|
|
}
|
|
|
|
//cache display format stuff
|
|
let timeZoneName = window.$gz.locale.getBrowserTimeZoneName();
|
|
let languageName = window.$gz.locale.getBrowserLanguages();
|
|
let hour12 = window.$gz.store.state.locale.hour12;
|
|
let currencyName = window.$gz.store.state.locale.currencyName;
|
|
|
|
//comes as an array of arrays, needs to leave as an array of objects representing each row
|
|
for (let iRow = 0; iRow < listData.length; iRow++) {
|
|
let row = listData[iRow];
|
|
//iterate row and build object representing row data keyed to index
|
|
|
|
//container object for row
|
|
//id will be set later when code below encounters the id column which could be in any position but is identified by it's rid property
|
|
let o = { id: undefined, columns: {} };
|
|
|
|
for (let iColumn = 0; iColumn < row.length; iColumn++) {
|
|
let column = row[iColumn];
|
|
|
|
//rowId?
|
|
if (column.rid) {
|
|
o.id = column.i;
|
|
}
|
|
|
|
let dataType = columndefinitions[iColumn].dt;
|
|
let display = column.v;
|
|
/*
|
|
public enum UiFieldDataType : int
|
|
{
|
|
NoType = 0,
|
|
DateTime = 1,
|
|
Date = 2,
|
|
Time = 3,
|
|
Text = 4,
|
|
Integer = 5,
|
|
Bool = 6,
|
|
Decimal = 7,
|
|
Currency = 8,
|
|
Tags = 9,
|
|
Enum = 10,
|
|
EmailAddress = 11,
|
|
HTTP = 12,
|
|
InternalId = 13
|
|
|
|
}
|
|
*/
|
|
switch (dataType) {
|
|
case 1: //datetime format to shortdatetime
|
|
display = window.$gz.locale.utcDateToShortDateAndTimeLocalized(
|
|
display,
|
|
timeZoneName,
|
|
languageName,
|
|
hour12
|
|
);
|
|
break;
|
|
case 2: //date only
|
|
display = window.$gz.locale.utcDateToShortDateLocalized(
|
|
display,
|
|
timeZoneName,
|
|
languageName
|
|
);
|
|
break;
|
|
case 3: //time only
|
|
display = window.$gz.locale.utcDateToShortTimeLocalized(
|
|
display,
|
|
timeZoneName,
|
|
languageName,
|
|
hour12
|
|
);
|
|
break;
|
|
case 7: //decimal
|
|
display = window.$gz.locale.decimalLocalized(display, languageName);
|
|
break;
|
|
case 8: //currency
|
|
display = window.$gz.locale.currencyLocalized(
|
|
display,
|
|
languageName,
|
|
currencyName
|
|
);
|
|
break;
|
|
case 9: //tags
|
|
if (display && display.length > 0) {
|
|
display = display.join(", ");
|
|
} else {
|
|
display = "";
|
|
}
|
|
|
|
break;
|
|
case 10: //enum
|
|
display = window.$gz.enums.get(
|
|
columndefinitions[iColumn].et,
|
|
display
|
|
);
|
|
break;
|
|
default:
|
|
//do nothing, allow it to stay as is (checkbox, plain text etc)
|
|
}
|
|
//build the row column object vm will be used by the datatable
|
|
let columnObject = {
|
|
v: display,
|
|
t: dataType,
|
|
key: iRow + "-" + iColumn
|
|
};
|
|
//is the source dtalist field openable? If so it will have an i property set for it's ID and we already know the types to open from the column headers data
|
|
//so between the two we can make a clickable button in the grid vm triggers a function with the column index and the id and vm in turn will bubble up the event to open vm
|
|
//object
|
|
if (column.i) {
|
|
columnObject["i"] = column.i;
|
|
}
|
|
o.columns["c" + iColumn.toString()] = columnObject;
|
|
//Is:
|
|
//Headers: [ { "text": "Name", "value": "c1" }, { "text": "Serial #", "value": "c2" },
|
|
//Records: [ { "id": 1, "c1": "Incredible Metal Fish 76", "c2": 1, "c3": "$877.8", "c4": "AuthorizationRoles.65536", "c5": "2020-01-30 01:53:57 AM", "c6": "Yup", "c7": "Virgil Strosin 74" }, { "id": 2, "c1": "Practical Plastic Bike 77", "c2": 2, "
|
|
//CHANGE TO:
|
|
//Headers: [ { "text": "Name", "value": "c1.v" }, { "text": "Serial #", "value": "c2" },
|
|
//Records: [ { "id": 1,columns: {"c1": {v:"Incredible Metal Fish 76",t:THETYPE,key:ROWID-COLUMNID e.g. 1-1,1-2,2-1 etc}, "c2": 1, "c3": "$877.8", "c4": "AuthorizationRoles.65536", "c5": "2020-01-30 01:53:57 AM", "c6": "Yup", "c7": "Virgil Strosin 74" }, { "id": 2, "c1": "Practical Plastic Bike 77", "c2": 2, "
|
|
//THis way can have v-if for each column vm changes based on type and then has it's shit figured out
|
|
}
|
|
ret.push(o);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Ensures column names are present in translation table
|
|
//
|
|
async function fetchTranslatedHeaderNames(columnData) {
|
|
if (!columnData) {
|
|
return;
|
|
}
|
|
let headerKeys = [];
|
|
for (let i = 0; i < columnData.length; i++) {
|
|
let cm = columnData[i];
|
|
headerKeys.push(cm.cm);
|
|
}
|
|
//Now fetch all the keys and await the response before returning
|
|
await window.$gz.translation.cacheTranslations(headerKeys).then(() => {
|
|
return;
|
|
});
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Ensures column enums are present in enums list in store
|
|
//
|
|
async function fetchEnums(columnData) {
|
|
if (!columnData) {
|
|
return;
|
|
}
|
|
let headerKeys = [];
|
|
for (let i = 1; i < columnData.length; i++) {
|
|
let cm = columnData[i];
|
|
if (cm.et) {
|
|
await window.$gz.enums.fetchEnumList(cm.et);
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
//
|
|
//
|
|
function initForm(vm) {
|
|
return new Promise(function(resolve, reject) {
|
|
(async function() {
|
|
try {
|
|
await populateSelectionLists(vm);
|
|
await loadFormSettings(vm);
|
|
} catch (err) {
|
|
reject(err);
|
|
}
|
|
resolve();
|
|
})();
|
|
});
|
|
}
|
|
|
|
////////////////////
|
|
//
|
|
function populateSelectionLists(vm) {
|
|
//http://localhost:7575/api/v8/data-list-view/viewlist?ListKey=TestWidgetDataList
|
|
return window.$gz.api
|
|
.get("data-list-view/viewlist?ListKey=" + vm.dataListKey)
|
|
.then(res => {
|
|
if (res.error) {
|
|
window.$gz.errorHandler.handleFormError(res.error, vm);
|
|
} else {
|
|
vm.selectLists.listViews = res.data;
|
|
window.$gz.form.addNoSelectionItem(vm.selectLists.listViews);
|
|
}
|
|
});
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Fetch and cache list view
|
|
//
|
|
function fetchListView(vm) {
|
|
if (!vm.listViewId) {
|
|
return;
|
|
}
|
|
return window.$gz.api.get("data-list-view/" + vm.listViewId).then(res => {
|
|
if (res.error) {
|
|
window.$gz.errorHandler.handleFormError(res.error, vm);
|
|
} else {
|
|
vm.listView = res.data.listView;
|
|
}
|
|
});
|
|
}
|
|
|
|
////////////////////
|
|
//
|
|
function saveFormSettings(vm) {
|
|
let unsavedlv = vm.listView;
|
|
let cachedlv = vm.listView;
|
|
|
|
if (vm.listViewId == 0) {
|
|
//we aren't using any listview
|
|
unsavedlv = undefined;
|
|
cachedlv = undefined;
|
|
}
|
|
|
|
if (vm.listViewId == -1) {
|
|
//we have an unsaved one in use so there is no need for a cached one
|
|
cachedlv = undefined;
|
|
}
|
|
|
|
if (vm.listViewId > 0) {
|
|
//we are using a saved lv so save cached one and clear anything in unsaved one
|
|
unsavedlv = undefined;
|
|
}
|
|
|
|
window.$gz.form.setFormSettings(vm.formKey, {
|
|
temp: { page: vm.dataTablePagingOptions.page, cachedListView: cachedlv },
|
|
saved: {
|
|
itemsPerPage: vm.dataTablePagingOptions.itemsPerPage,
|
|
dataTable: { listViewId: vm.listViewId, unsavedListView: unsavedlv }
|
|
}
|
|
});
|
|
}
|
|
|
|
////////////////////
|
|
//
|
|
function loadFormSettings(vm) {
|
|
let formSettings = window.$gz.form.getFormSettings(vm.formKey);
|
|
|
|
//process SAVED formsettings
|
|
if (formSettings.saved) {
|
|
if (formSettings.saved.itemsPerPage) {
|
|
vm.dataTablePagingOptions.itemsPerPage = formSettings.saved.itemsPerPage;
|
|
}
|
|
if (formSettings.saved.dataTable.listViewId != null) {
|
|
vm.listViewId = formSettings.saved.dataTable.listViewId;
|
|
}
|
|
if (vm.listViewId == 0) {
|
|
//default view, not unsaved and not saved
|
|
vm.listView = undefined;
|
|
}
|
|
if (vm.listViewId == -1) {
|
|
//-1 is code for unsaved list view
|
|
//check if there is a local copy of a listview vm was edited but not saved
|
|
if (formSettings.saved.dataTable.unsavedListView != null) {
|
|
//add UNSAVED FILTER if -1
|
|
|
|
vm.selectLists.listViews.unshift({
|
|
name: vm.$ay.t("FilterUnsaved"),
|
|
id: -1
|
|
});
|
|
|
|
vm.listView = formSettings.saved.dataTable.unsavedListView;
|
|
} else {
|
|
//listviewid is for unsaved but we have no unsaved so fix that up
|
|
vm.listView = undefined;
|
|
vm.listViewId = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//process TEMP form settings
|
|
if (formSettings.temp) {
|
|
if (formSettings.temp.page) {
|
|
vm.dataTablePagingOptions.page = formSettings.temp.page;
|
|
}
|
|
|
|
//check for cached local copy of saved list view in use
|
|
if (vm.listViewId > 0) {
|
|
//0=no list view, -1=unsaved list view so any number greater than zero means there sb a cached local copy of a saved list view
|
|
if (formSettings.temp && formSettings.temp.cachedListView != null) {
|
|
vm.listView = formSettings.temp.cachedListView;
|
|
} else {
|
|
//fetch it and cache it
|
|
return fetchListView(vm, vm.listViewId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*//EXAMPLE FROM INTEGRATION TEST OF BETWEEN TWO DATES
|
|
// dynamic fitem = new JObject();
|
|
// fitem.op = Util.OpGreaterThanOrEqualTo;
|
|
// fitem.value = new DateTime(2019, 3, 12, 10, 0, 0).ToOffsetAdjustedUniversalTime();
|
|
// items.Add(fitem);
|
|
|
|
// fitem = new JObject();
|
|
// fitem.op = Util.OpLessThanOrEqualTo;
|
|
// fitem.value = new DateTime(2019, 3, 12, 11, 0, 0).ToOffsetAdjustedUniversalTime();
|
|
// items.Add(fitem); */
|
|
////////////////////
|
|
//
|
|
function untokenizeListView(lvJson) {
|
|
//if it has one or more tokens
|
|
//iterate the array and build a new array with substituted tokens with the correct date and time in them
|
|
|
|
//format of a date token filter
|
|
//[{"fld":"widgetname"},{"fld":"widgetstartdate","filter":{"items":[{"op":"=","value":"*past90days*","token":true}]}},{"fld":"widgetenddate"}]
|
|
if (lvJson == null) {
|
|
return lvJson;
|
|
}
|
|
//See if it has any date tokens
|
|
if (lvJson.indexOf('"token":true') == -1) {
|
|
return lvJson;
|
|
}
|
|
|
|
//we have one or more tokens, substitute them in the filter array
|
|
let ret = [];
|
|
let lv = JSON.parse(lvJson);
|
|
|
|
//iterate the incoming and copy to the outgoing directly
|
|
//except if a date token filter then substitute our own filter object in place
|
|
for (let ilv = 0; ilv < lv.length; ilv++) {
|
|
//listview object
|
|
let lvo = lv[ilv];
|
|
//instantiate return object
|
|
let reto = {};
|
|
//copy over field name to return object
|
|
reto.fld = lvo.fld;
|
|
//sort?
|
|
if (lvo.sort) {
|
|
reto.sort = lvo.sort;
|
|
}
|
|
//does it have a filter?
|
|
if (lvo.filter) {
|
|
//yes, so copy / transform as required
|
|
|
|
//create an empty filter items array on return object
|
|
reto.filter = { items: [] };
|
|
|
|
//"any" property set?
|
|
if (lvo.filter.any) {
|
|
reto.filter.any = true;
|
|
}
|
|
|
|
//iterate the filter items in the source lvo object
|
|
for (let j = 0; j < lvo.filter.items.length; j++) {
|
|
//get this filter item
|
|
let fi = lvo.filter.items[j];
|
|
//no token shortcut
|
|
if (!fi.token) {
|
|
//just copy it out
|
|
reto.filter.items.push(fi);
|
|
} else {
|
|
//it has a date token so let's build it out
|
|
//filter item value contains the token, op is always equals
|
|
let filterDates = relativeDatefilterCalculator.tokenToDates(fi.value);
|
|
//create and add a new filter item for each not undefined value
|
|
//{ after: undefined, before: undefined }
|
|
//AFTER DATE?
|
|
if (filterDates.after) {
|
|
reto.filter.items.push({
|
|
op: ">", //was GreaterThanOrEqualTo but that doesn't make sense, it's only greater than was seeing wrong return data as relative date filter already adjusts for boundaries
|
|
value: filterDates.after
|
|
});
|
|
}
|
|
//BEFORE DATE?
|
|
if (filterDates.before) {
|
|
reto.filter.items.push({
|
|
op: "<", //was LessThanOrEqualTo but see above for after ===-----^
|
|
value: filterDates.before
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
//end of has filter if condition
|
|
}
|
|
|
|
//push the return object into the return array
|
|
ret.push(reto);
|
|
//end of iterate lv loop
|
|
}
|
|
|
|
return JSON.stringify(ret);
|
|
} //[{"fld":"widgetname"},{"fld":"widgetstartdate","filter":{"items":[{"op":"=","value":"*past90days*","token":true}]}},{"fld":"widgetenddate"}]
|
|
</script>
|