This commit is contained in:
@@ -3,7 +3,6 @@ const role = authorizationroles.AUTHORIZATION_ROLES;
|
|||||||
export default {
|
export default {
|
||||||
registry: [
|
registry: [
|
||||||
{
|
{
|
||||||
id: "dash-today-reminders",
|
|
||||||
roles: [
|
roles: [
|
||||||
role.BizAdmin,
|
role.BizAdmin,
|
||||||
role.BizAdminRestricted,
|
role.BizAdminRestricted,
|
||||||
@@ -25,7 +24,6 @@ export default {
|
|||||||
settings: {}
|
settings: {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "dash-today-reviews",
|
|
||||||
roles: [
|
roles: [
|
||||||
role.BizAdmin,
|
role.BizAdmin,
|
||||||
role.BizAdminRestricted,
|
role.BizAdminRestricted,
|
||||||
@@ -47,7 +45,6 @@ export default {
|
|||||||
settings: {}
|
settings: {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "dash-today-scheduled-wo",
|
|
||||||
roles: [role.Tech, role.TechRestricted],
|
roles: [role.Tech, role.TechRestricted],
|
||||||
title: "DashboardScheduled",
|
title: "DashboardScheduled",
|
||||||
type: "GzDashTodayScheduledWo",
|
type: "GzDashTodayScheduledWo",
|
||||||
@@ -56,35 +53,32 @@ export default {
|
|||||||
settings: {}
|
settings: {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "dash-labor-hours-personal",
|
|
||||||
roles: [role.Tech, role.TechRestricted],
|
roles: [role.Tech, role.TechRestricted],
|
||||||
title: "Labor hours",
|
title: "WorkOrderItemLaborList",
|
||||||
type: "GzDashLaborHoursPersonal",
|
type: "GzDashLaborHoursPersonal",
|
||||||
scheduleableUserOnly: true,
|
scheduleableUserOnly: true,
|
||||||
singleOnly: false,
|
singleOnly: false,
|
||||||
settings: {
|
settings: {
|
||||||
customTitle: null,
|
customTitle: null,
|
||||||
dateRange: "*thismonth*",
|
timeSpan: "*thismonth*",
|
||||||
unit: "day",
|
interval: "day",
|
||||||
color: "#000000"
|
color: "#00205B"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "dash-labor-hours-personal-bar",
|
|
||||||
roles: [role.Tech, role.TechRestricted],
|
roles: [role.Tech, role.TechRestricted],
|
||||||
title: "Labor hours - bar",
|
title: "WorkOrderItemLaborList",
|
||||||
type: "GzDashLaborHoursPersonalBar",
|
type: "GzDashLaborHoursPersonalBar",
|
||||||
scheduleableUserOnly: true,
|
scheduleableUserOnly: true,
|
||||||
singleOnly: false,
|
singleOnly: false,
|
||||||
settings: {
|
settings: {
|
||||||
customTitle: null,
|
customTitle: null,
|
||||||
dateRange: "*thismonth*",
|
timeSpan: "*thismonth*",
|
||||||
unit: "day",
|
interval: "day",
|
||||||
color: "#000000"
|
color: "#00205B"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "TestBarWidgetCountByUserType",
|
|
||||||
roles: [
|
roles: [
|
||||||
role.BizAdmin,
|
role.BizAdmin,
|
||||||
role.BizAdminRestricted,
|
role.BizAdminRestricted,
|
||||||
@@ -98,7 +92,6 @@ export default {
|
|||||||
settings: { customTitle: "my custom title" }
|
settings: { customTitle: "my custom title" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "TestLineWidgetMonthlyTotalPrice",
|
|
||||||
roles: [
|
roles: [
|
||||||
role.BizAdmin,
|
role.BizAdmin,
|
||||||
role.BizAdminRestricted,
|
role.BizAdminRestricted,
|
||||||
@@ -126,7 +119,7 @@ export default {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ret.push({
|
ret.push({
|
||||||
id: item.id,
|
id: i,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
type: item.type,
|
type: item.type,
|
||||||
singleOnly: item.singleOnly,
|
singleOnly: item.singleOnly,
|
||||||
|
|||||||
@@ -168,7 +168,7 @@
|
|||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: Number,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
title: { type: String, default: null },
|
title: { type: String, default: null },
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<gz-dash
|
<gz-dash
|
||||||
icon="$ayiUser"
|
icon="$ayiUser"
|
||||||
:show-context-button="true"
|
:show-context-button="true"
|
||||||
:update-frequency="600000"
|
:update-frequency="900000"
|
||||||
v-bind="[$props, $attrs]"
|
v-bind="[$props, $attrs]"
|
||||||
@dash-refresh="getDataFromApi()"
|
@dash-refresh="getDataFromApi()"
|
||||||
@dash-context="showContext()"
|
@dash-context="showContext()"
|
||||||
@@ -10,17 +10,6 @@
|
|||||||
>
|
>
|
||||||
<template slot="main">
|
<template slot="main">
|
||||||
<div>
|
<div>
|
||||||
<!-- <gz-chart-line
|
|
||||||
:width="400"
|
|
||||||
:height="240"
|
|
||||||
:chart-data="obj"
|
|
||||||
:options="{
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
legend: { display: false }
|
|
||||||
}"
|
|
||||||
></gz-chart-line> -->
|
|
||||||
|
|
||||||
<gz-chart-bar
|
<gz-chart-bar
|
||||||
:width="400"
|
:width="400"
|
||||||
:height="330"
|
:height="330"
|
||||||
@@ -46,21 +35,20 @@
|
|||||||
</v-card-title>
|
</v-card-title>
|
||||||
|
|
||||||
<v-card-text style="height: 500px;">
|
<v-card-text style="height: 500px;">
|
||||||
{{ settings }}
|
|
||||||
<v-select
|
<v-select
|
||||||
v-model="localSettings.dateRange"
|
v-model="localSettings.timeSpan"
|
||||||
:items="selectLists.dateFilterTokens"
|
:items="selectLists.dateFilterTokens"
|
||||||
item-text="name"
|
item-text="name"
|
||||||
item-value="id"
|
item-value="id"
|
||||||
:label="$ay.t('TimeSpanDateRange')"
|
:label="$ay.t('TimeSpan')"
|
||||||
></v-select>
|
></v-select>
|
||||||
|
|
||||||
<v-select
|
<v-select
|
||||||
v-model="localSettings.unit"
|
v-model="localSettings.interval"
|
||||||
:items="selectLists.units"
|
:items="selectLists.units"
|
||||||
item-text="name"
|
item-text="name"
|
||||||
item-value="id"
|
item-value="id"
|
||||||
:label="$ay.t('Unit')"
|
:label="$ay.t('Interval')"
|
||||||
></v-select>
|
></v-select>
|
||||||
|
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@@ -98,7 +86,6 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import GzDash from "./dash-base.vue";
|
import GzDash from "./dash-base.vue";
|
||||||
//import Palette from "../api/palette";
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
GzDash
|
GzDash
|
||||||
@@ -125,7 +112,7 @@ export default {
|
|||||||
{
|
{
|
||||||
type: "time",
|
type: "time",
|
||||||
time: {
|
time: {
|
||||||
unit: this.settings.unit
|
unit: "day"
|
||||||
},
|
},
|
||||||
gridLines: {
|
gridLines: {
|
||||||
drawOnChartArea: false
|
drawOnChartArea: false
|
||||||
@@ -175,71 +162,22 @@ export default {
|
|||||||
updateSettings: function() {
|
updateSettings: function() {
|
||||||
//copy settings from local to
|
//copy settings from local to
|
||||||
this.settings.customTitle = this.localSettings.customTitle;
|
this.settings.customTitle = this.localSettings.customTitle;
|
||||||
this.settings.dateRange = this.localSettings.dateRange;
|
this.settings.timeSpan = this.localSettings.timeSpan;
|
||||||
this.settings.unit = this.localSettings.unit;
|
this.settings.interval = this.localSettings.interval;
|
||||||
this.settings.color = this.localSettings.color;
|
this.settings.color = this.localSettings.color;
|
||||||
this.$emit("dash-change");
|
this.$emit("dash-change"); //trigger save to server
|
||||||
this.context = false;
|
this.context = false;
|
||||||
this.getDataFromApi();
|
this.getDataFromApi();
|
||||||
},
|
},
|
||||||
|
|
||||||
async getDataFromApi() {
|
async getDataFromApi() {
|
||||||
//todo: need a equivalent of a datalist at the server but that can take the minimal criteria offered here and return the data easily digestable
|
|
||||||
//do not want the client end to have to do math or anything and also it needs to drive reporting of the same type as the widget display
|
|
||||||
//so as similar as possible to the datatable system but handles the math and summarizing ideally in the db server itself
|
|
||||||
/*
|
|
||||||
TODO:
|
|
||||||
DYNAMIC FROM SETS
|
|
||||||
handle params (time range, currentusertoken,period)
|
|
||||||
enforce current user only (I guess that's built in if use token or leave out token because it's assumed in which case rename to WorkOrderItemLaborQuantitySummaryPERSONAL )
|
|
||||||
ACTUAL CHART WORKING
|
|
||||||
line chart, can it work with this data, does it need adjustment to locale?
|
|
||||||
scroll? Vertical better than horizontal? Select bar OR line?
|
|
||||||
|
|
||||||
|
|
||||||
REPORTING
|
|
||||||
v.nexxt?
|
|
||||||
charts lib at server?
|
|
||||||
|
|
||||||
|
|
||||||
DateTrunc https://www.postgresqltutorial.com/postgresql-date_trunc/ will respect changes of month or year or whatever so this works
|
|
||||||
|
|
||||||
SELECT row_to_json(t) as res from (
|
|
||||||
|
|
||||||
select SUM(AWORKORDERITEMLABOR.serviceratequantity) SERVICERATESUM, date_trunc('month',AWORKORDERITEMLABOR.servicestartdate) timeframe
|
|
||||||
|
|
||||||
FROM AWORKORDER
|
|
||||||
LEFT JOIN AWORKORDERITEM ON AWORKORDER.ID = AWORKORDERITEM.WORKORDERID
|
|
||||||
LEFT JOIN AWORKORDERITEMLABOR ON AWORKORDERITEM.ID = AWORKORDERITEMLABOR.WORKORDERITEMID
|
|
||||||
WHERE AWORKORDERITEMLABOR.userid = 10
|
|
||||||
|
|
||||||
GROUP BY timeframe
|
|
||||||
ORDER BY timeframe ASC
|
|
||||||
) t
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// let tt = {
|
|
||||||
// KPIName: "WorkOrderItemLaborQuantitySummary",
|
|
||||||
// criteria: {
|
|
||||||
// dateRange: this.settings.dateRange,
|
|
||||||
// unit: this.settings.unit
|
|
||||||
// },
|
|
||||||
// clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp()
|
|
||||||
// };
|
|
||||||
// console.log(tt);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.errorMessage = null;
|
this.errorMessage = null;
|
||||||
const res = await window.$gz.api.post("kpi", {
|
const res = await window.$gz.api.post("kpi", {
|
||||||
KPIName: "WorkOrderItemLaborQuantitySummary",
|
KPIName: "WorkOrderItemLaborQuantitySummary",
|
||||||
criteria: {
|
criteria: {
|
||||||
dateRange: this.settings.dateRange,
|
timeSpan: this.settings.timeSpan,
|
||||||
unit: this.settings.unit
|
interval: this.settings.interval
|
||||||
},
|
},
|
||||||
clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp()
|
clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp()
|
||||||
});
|
});
|
||||||
@@ -247,6 +185,8 @@ ORDER BY timeframe ASC
|
|||||||
this.errorMessage = res.error;
|
this.errorMessage = res.error;
|
||||||
} else {
|
} else {
|
||||||
// console.log(res);
|
// console.log(res);
|
||||||
|
this.chartOptions.scales.xAxes[0].time.unit = this.settings.interval;
|
||||||
|
//console.log(this.chartOptions);
|
||||||
this.obj = res.data;
|
this.obj = res.data;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -315,7 +255,10 @@ async function fetchTranslatedText() {
|
|||||||
"DateRangePreviousYearNextMonth",
|
"DateRangePreviousYearNextMonth",
|
||||||
"TimeSpanDays",
|
"TimeSpanDays",
|
||||||
"TimeSpanMonths",
|
"TimeSpanMonths",
|
||||||
"WorkOrderItemLaborServiceRateQuantity"
|
"WorkOrderItemLaborServiceRateQuantity",
|
||||||
|
"Name",
|
||||||
|
"TimeSpan",
|
||||||
|
"Interval"
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,8 +344,48 @@ function populateSelectionLists(vm) {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
var crit=(string)options.Criteria["dateRange"];
|
|
||||||
|
|
||||||
*/
|
/* //todo: need a equivalent of a datalist at the server but that can take the minimal criteria offered here and return the data easily digestable
|
||||||
|
//do not want the client end to have to do math or anything and also it needs to drive reporting of the same type as the widget display
|
||||||
|
//so as similar as possible to the datatable system but handles the math and summarizing ideally in the db server itself
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
DYNAMIC FROM SETS
|
||||||
|
handle params (time range, currentusertoken,period)
|
||||||
|
enforce current user only (I guess that's built in if use token or leave out token because it's assumed in which case rename to WorkOrderItemLaborQuantitySummaryPERSONAL )
|
||||||
|
ACTUAL CHART WORKING
|
||||||
|
line chart, can it work with this data, does it need adjustment to locale?
|
||||||
|
scroll? Vertical better than horizontal? Select bar OR line?
|
||||||
|
|
||||||
|
|
||||||
|
REPORTING
|
||||||
|
v.nexxt?
|
||||||
|
charts lib at server?
|
||||||
|
|
||||||
|
|
||||||
|
DateTrunc https://www.postgresqltutorial.com/postgresql-date_trunc/ will respect changes of month or year or whatever so this works
|
||||||
|
|
||||||
|
SELECT row_to_json(t) as res from (
|
||||||
|
|
||||||
|
select SUM(AWORKORDERITEMLABOR.serviceratequantity) SERVICERATESUM, date_trunc('month',AWORKORDERITEMLABOR.servicestartdate) timeframe
|
||||||
|
|
||||||
|
FROM AWORKORDER
|
||||||
|
LEFT JOIN AWORKORDERITEM ON AWORKORDER.ID = AWORKORDERITEM.WORKORDERID
|
||||||
|
LEFT JOIN AWORKORDERITEMLABOR ON AWORKORDERITEM.ID = AWORKORDERITEMLABOR.WORKORDERITEMID
|
||||||
|
WHERE AWORKORDERITEMLABOR.userid = 10
|
||||||
|
|
||||||
|
GROUP BY timeframe
|
||||||
|
ORDER BY timeframe ASC
|
||||||
|
) t
|
||||||
|
|
||||||
|
|
||||||
|
// let tt = {
|
||||||
|
// KPIName: "WorkOrderItemLaborQuantitySummary",
|
||||||
|
// criteria: {
|
||||||
|
// timeSpan: this.settings.timeSpan,
|
||||||
|
// unit: this.settings.unit
|
||||||
|
// },
|
||||||
|
// clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp()
|
||||||
|
// };
|
||||||
|
// console.log(tt); */
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
<v-card-text style="height: 500px;">
|
<v-card-text style="height: 500px;">
|
||||||
{{ settings }}
|
{{ settings }}
|
||||||
<v-select
|
<v-select
|
||||||
v-model="localSettings.dateRange"
|
v-model="localSettings.timeSpan"
|
||||||
:items="selectLists.dateFilterTokens"
|
:items="selectLists.dateFilterTokens"
|
||||||
item-text="name"
|
item-text="name"
|
||||||
item-value="id"
|
item-value="id"
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
></v-select>
|
></v-select>
|
||||||
|
|
||||||
<v-select
|
<v-select
|
||||||
v-model="localSettings.unit"
|
v-model="localSettings.interval"
|
||||||
:items="selectLists.units"
|
:items="selectLists.units"
|
||||||
item-text="name"
|
item-text="name"
|
||||||
item-value="id"
|
item-value="id"
|
||||||
@@ -126,7 +126,7 @@ export default {
|
|||||||
{
|
{
|
||||||
type: "time",
|
type: "time",
|
||||||
time: {
|
time: {
|
||||||
unit: this.settings.unit
|
unit: this.settings.interval
|
||||||
},
|
},
|
||||||
gridLines: {
|
gridLines: {
|
||||||
drawOnChartArea: false
|
drawOnChartArea: false
|
||||||
@@ -181,8 +181,8 @@ export default {
|
|||||||
updateSettings: function() {
|
updateSettings: function() {
|
||||||
//copy settings from local to
|
//copy settings from local to
|
||||||
this.settings.customTitle = this.localSettings.customTitle;
|
this.settings.customTitle = this.localSettings.customTitle;
|
||||||
this.settings.dateRange = this.localSettings.dateRange;
|
this.settings.timeSpan = this.localSettings.timeSpan;
|
||||||
this.settings.unit = this.localSettings.unit;
|
this.settings.interval = this.localSettings.interval;
|
||||||
this.settings.color = this.localSettings.color;
|
this.settings.color = this.localSettings.color;
|
||||||
|
|
||||||
this.$emit("dash-change");
|
this.$emit("dash-change");
|
||||||
@@ -235,8 +235,8 @@ ORDER BY timeframe ASC
|
|||||||
// let tt = {
|
// let tt = {
|
||||||
// KPIName: "WorkOrderItemLaborQuantitySummary",
|
// KPIName: "WorkOrderItemLaborQuantitySummary",
|
||||||
// criteria: {
|
// criteria: {
|
||||||
// dateRange: this.settings.dateRange,
|
// timeSpan: this.settings.timeSpan,
|
||||||
// unit: this.settings.unit
|
// interval: this.settings.interval
|
||||||
// },
|
// },
|
||||||
// clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp()
|
// clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp()
|
||||||
// };
|
// };
|
||||||
@@ -247,8 +247,8 @@ ORDER BY timeframe ASC
|
|||||||
const res = await window.$gz.api.post("kpi", {
|
const res = await window.$gz.api.post("kpi", {
|
||||||
KPIName: "WorkOrderItemLaborQuantitySummary",
|
KPIName: "WorkOrderItemLaborQuantitySummary",
|
||||||
criteria: {
|
criteria: {
|
||||||
dateRange: this.settings.dateRange,
|
timeSpan: this.settings.timeSpan,
|
||||||
unit: this.settings.unit
|
interval: this.settings.interval
|
||||||
},
|
},
|
||||||
clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp()
|
clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp()
|
||||||
});
|
});
|
||||||
@@ -411,7 +411,7 @@ function populateSelectionLists(vm) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
var crit=(string)options.Criteria["dateRange"];
|
var crit=(string)options.Criteria["timeSpan"];
|
||||||
|
|
||||||
*/
|
*/
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -44,8 +44,8 @@
|
|||||||
</v-row>
|
</v-row>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col
|
<v-col
|
||||||
v-for="(item, i) in effectiveView"
|
v-for="item in effectiveView"
|
||||||
:key="i"
|
:key="item.id"
|
||||||
class="d-flex child-flex"
|
class="d-flex child-flex"
|
||||||
cols="12"
|
cols="12"
|
||||||
sm="6"
|
sm="6"
|
||||||
@@ -54,7 +54,6 @@
|
|||||||
>
|
>
|
||||||
<component
|
<component
|
||||||
:is="item.type"
|
:is="item.type"
|
||||||
:ref="item.ref"
|
|
||||||
v-bind="item"
|
v-bind="item"
|
||||||
:max-list-items="10"
|
:max-list-items="10"
|
||||||
@dash-remove="dashRemove"
|
@dash-remove="dashRemove"
|
||||||
@@ -214,16 +213,31 @@ export default {
|
|||||||
},
|
},
|
||||||
addItem: function(item) {
|
addItem: function(item) {
|
||||||
this.showSelector = false;
|
this.showSelector = false;
|
||||||
item.ref = "db" + Date.now();
|
const newItem = JSON.parse(JSON.stringify(item));
|
||||||
this.effectiveView.push(item);
|
newItem.id = Date.now();
|
||||||
|
this.effectiveView.push(newItem);
|
||||||
this.saveView();
|
this.saveView();
|
||||||
},
|
},
|
||||||
availableItems: function() {
|
availableItems: function() {
|
||||||
const allItems = DashRegistry.availableItems();
|
const allItems = DashRegistry.availableItems();
|
||||||
const newItems = allItems.filter(
|
// console.log("availableItems:allItems", JSON.stringify(allItems));
|
||||||
z => !this.effectiveView.find(m => m.id == z.id && z.singleOnly)
|
// console.log(
|
||||||
);
|
// "availableItems:effectiveView",
|
||||||
return newItems;
|
// JSON.stringify(this.effectiveView)
|
||||||
|
// );
|
||||||
|
const ret = [];
|
||||||
|
allItems.forEach(z => {
|
||||||
|
if (!z.singleOnly) {
|
||||||
|
ret.push(z);
|
||||||
|
} else {
|
||||||
|
if (this.effectiveView.findIndex(m => m.type == z.type) == -1) {
|
||||||
|
ret.push(z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log("availableItems:ret", JSON.stringify(ret));
|
||||||
|
return ret;
|
||||||
},
|
},
|
||||||
async getDataFromApi() {
|
async getDataFromApi() {
|
||||||
const vm = this;
|
const vm = this;
|
||||||
@@ -246,7 +260,7 @@ export default {
|
|||||||
const availableItems = DashRegistry.availableItems();
|
const availableItems = DashRegistry.availableItems();
|
||||||
//filter out any that are deprecated or no longer accessible due to role change
|
//filter out any that are deprecated or no longer accessible due to role change
|
||||||
const allowedView = savedView.filter(z =>
|
const allowedView = savedView.filter(z =>
|
||||||
availableItems.find(m => m.id == z.id)
|
availableItems.find(m => m.type == z.type)
|
||||||
);
|
);
|
||||||
vm.effectiveView = allowedView;
|
vm.effectiveView = allowedView;
|
||||||
generateMenu(vm);
|
generateMenu(vm);
|
||||||
|
|||||||
Reference in New Issue
Block a user