This commit is contained in:
2022-02-28 20:30:31 +00:00
parent cbf971db77
commit fc385a53e7
4 changed files with 401 additions and 2 deletions

View File

@@ -47,15 +47,41 @@ seeder wo need new fields and generate data to show off and test kpi widgets and
https://www.ayanova.com/AyaNova7webHelp/index.html?dashboard.htm https://www.ayanova.com/AyaNova7webHelp/index.html?dashboard.htm
time zone conversion at client
- Widgets to make for beta in order of priority - Widgets to make for beta in order of priority
**MUST HAVE*** **MUST HAVE***
* LIST Unassigned work orders list for service manager * LIST not scheduled work orders list for service manager [wo#], [date?best one],[customer],[summary]
criteria?
tags, currentstatus?
description:
"Not assigned provides links to service workorders that have not yet been assigned to any schedulable resources." "Not assigned provides links to service workorders that have not yet been assigned to any schedulable resources."
wo that is not a closed status, has no techs assigned to it in woitemscheduledusers (null or not extant) wo that is not a closed status, has no techs assigned to it in woitemscheduledusers (null or not extant)
oldest to newest in list, maybe just a limited number like most recent 50 or something oldest to newest in list, maybe just a limited number like most recent 50 or something
more button opens grid query for it if possible more button opens grid query for it if possible
Query stuff:
SELECT AWORKORDER.SERIAL,
AWORKORDER.SERVICEDATE,
ACUSTOMER.NAME,
AWORKORDER.NOTES
FROM AWORKORDER
LEFT JOIN AWORKORDERITEM ON AWORKORDER.ID = AWORKORDERITEM.WORKORDERID
LEFT JOIN AWORKORDERITEMSCHEDULEDUSER ON AWORKORDERITEM.ID = AWORKORDERITEMSCHEDULEDUSER.WORKORDERITEMID
LEFT JOIN AWORKORDERSTATUS ON (AWORKORDER.LASTSTATUSID = AWORKORDERSTATUS.ID)
LEFT JOIN ACUSTOMER ON (AWORKORDER.CUSTOMERID = ACUSTOMER.ID)
WHERE (LASTSTATUSID IS NULL OR AWORKORDERSTATUS.COMPLETED = FALSE)
AND AWORKORDERITEMSCHEDULEDUSER.ID IS NULL
AND LASTSTATUSID = 2
ORDER BY AWORKORDER.ID ASC
SELECT count(*)
FROM aworkorder
LEFT JOIN AWORKORDERSTATUS ON (aworkorder.LASTSTATUSID = AWORKORDERSTATUS.ID)
WHERE (laststatusid is null or AWORKORDERSTATUS.completed = false)
* LIST CSR's with OPEN status only in reverse chronological order for service manager * LIST CSR's with OPEN status only in reverse chronological order for service manager
* BAR/LINE Billed hours for all users available to service manager roles * BAR/LINE Billed hours for all users available to service manager roles
dupe of personal one but can select user or all users dupe of personal one but can select user or all users

View File

@@ -1,7 +1,26 @@
import authorizationroles from "./authorizationroles"; import authorizationroles from "./authorizationroles";
const role = authorizationroles.AUTHORIZATION_ROLES; const role = authorizationroles.AUTHORIZATION_ROLES;
/*
*/
export default { export default {
registry: [ registry: [
{
roles: [
role.BizAdmin,
role.BizAdminRestricted,
role.ServiceRestricted,
role.Service,
role.Accounting,
role.Tech,
role.TechRestricted
],
title: "DashboardNotScheduled",
icon: "$ayiListAlt",
type: "GzDashWorkorderUnscheduledOpenList",
singleOnly: false,
settings: {}
},
{ {
roles: [ roles: [
role.BizAdmin, role.BizAdmin,

View File

@@ -0,0 +1,346 @@
<template>
<gz-dash
icon="$ayiUser"
:show-context-button="true"
:update-frequency="900000"
v-bind="[$props, $attrs]"
@dash-refresh="getDataFromApi()"
@dash-context="showContext()"
v-on="$listeners"
>
<template slot="main">
<div>
<gz-chart-bar :chart-data="chartData" :options="chartOptions" />
</div>
</template>
<template slot="settings">
<div></div>
<v-col v-if="context" cols="12">
<v-dialog
v-model="context"
scrollable
max-width="400px"
data-cy="dashSettings"
@keydown.esc="cancel"
>
<v-card elevation="24">
<v-card-title class="text-h5 lighten-2" primary-title>
<span> {{ $ay.t("Settings") }} </span>
</v-card-title>
<v-card-text>
<v-select
v-model="localSettings.timeSpan"
:items="selectLists.dateFilterTokens"
item-text="name"
item-value="id"
:label="$ay.t('TimeSpan')"
></v-select>
<v-select
v-model="localSettings.interval"
:items="selectLists.units"
item-text="name"
item-value="id"
:label="$ay.t('Interval')"
></v-select>
<gz-tag-picker
v-model="localSettings.wotags"
:label="$ay.t('Tags') + ' - ' + $ay.t('WorkOrder')"
></gz-tag-picker>
<gz-tag-picker
v-model="localSettings.woitemtags"
:label="$ay.t('Tags') + ' - ' + $ay.t('WorkOrderItem')"
></gz-tag-picker>
<v-text-field
v-model="localSettings.customTitle"
:label="$ay.t('Name')"
></v-text-field>
<v-color-picker
v-model="localSettings.color"
hide-mode-switch
hide-inputs
mode="hexa"
></v-color-picker>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-btn color="primary" text @click.native="context = false">{{
$ay.t("Cancel")
}}</v-btn>
<v-spacer></v-spacer>
<v-btn
color="primary"
text
class="ml-4"
@click="updateSettings"
>{{ $ay.t("Save") }}</v-btn
>
</v-card-actions>
</v-card>
</v-dialog>
</v-col>
</template>
</gz-dash>
</template>
<script>
import GzDash from "./dash-base.vue";
export default {
components: {
GzDash
},
props: {
settings: { type: Object, default: null }
},
data() {
return {
obj: {},
context: false,
localSettings: {},
selectLists: {
dateFilterTokens: [],
units: []
},
chartOptions: {
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [
{
type: "time",
time: {
unit: "day"
},
gridLines: {
drawOnChartArea: false
}
}
],
yAxes: [
{
gridLines: {
drawOnChartArea: false
},
ticks: {
beginAtZero: true
}
}
]
}
}
};
},
computed: {
chartData() {
return {
datasets: [
{
label: this.$ay.t("WorkOrderItemLaborServiceRateQuantity"),
backgroundColor: this.settings.color ?? "#000000",
data: this.obj
}
]
};
}
},
async created() {
await initWidget(this);
},
async mounted() {
//must be called from mounted to have refs available
//console.log("reminders-mounted");
await this.getDataFromApi();
},
methods: {
showContext: function() {
this.localSettings = window.$gz.util.deepCopySkip(this.settings);
this.context = true;
},
updateSettings: function() {
//copy settings from local to parent settings, need to do it this way or get error about mutating prop directly which is vexing and has no easy solution seemingly
this.settings.customTitle = this.localSettings.customTitle;
this.settings.timeSpan = this.localSettings.timeSpan;
this.settings.interval = this.localSettings.interval;
this.settings.color = this.localSettings.color;
this.settings.wotags = this.localSettings.wotags;
this.settings.woitemtags = this.localSettings.woitemtags;
this.$emit("dash-change"); //trigger save to server
this.context = false;
this.getDataFromApi();
},
async getDataFromApi() {
try {
this.errorMessage = null;
const res = await window.$gz.api.post("kpi", {
KPIName: "WorkOrderItemLaborQuantitySummary",
criteria: {
timeSpan: this.settings.timeSpan,
interval: this.settings.interval,
wotags: this.settings.wotags,
woitemtags: this.settings.woitemtags
},
clientTimeStamp: window.$gz.locale.clientLocalZoneTimeStamp()
});
if (res.error) {
this.errorMessage = res.error;
} else {
this.chartOptions.scales.xAxes[0].time.unit = this.settings.interval;
res.data.forEach(z => {
z.x = new Date(z.x) //convert to locale timezone and output in the closest thing to iso-8601 format
.toLocaleString("sv-SE", {
timeZone: this.timeZoneName
});
});
this.obj = res.data;
}
} catch (error) {
this.errorMessage = error.toString();
}
}
}
};
/////////////////////////////////
//
//
async function initWidget(vm) {
await fetchTranslatedText();
populateSelectionLists(vm);
}
//////////////////////////////////////////////////////////
//
// Ensures UI translated text is available
//
async function fetchTranslatedText() {
await window.$gz.translation.cacheTranslations([
"Filter",
"GridRowFilterDropDownBlanksItem",
"GridRowFilterDropDownNonBlanksItem",
"GridRowFilterDropDownEquals",
"GridRowFilterDropDownNotEquals",
"GridRowFilterDropDownDoesNotContain",
"GridRowFilterDropDownContains",
"DateRangeYesterday",
"DateRangeToday",
"DateRangeLastWeek",
"DateRangeThisWeek",
"DateRangeNextWeek",
"DateRangeLastMonth",
"DateRangeThisMonth",
"DateRangeNextMonth",
"DateRange14DayWindow",
"DateRangePast",
"DateRangeLastYear",
"DateRangeThisYear",
"DateRangeInTheLastThreeMonths",
"DateRangeInTheLastSixMonths",
"DateRangePastYear",
"DateRangePast90Days",
"DateRangePast30Days",
"DateRangePast7Days",
"DateRangePast24Hours",
"DateRangePast6Hours",
"DateRangeJanuary",
"DateRangeFebruary",
"DateRangeMarch",
"DateRangeApril",
"DateRangeMay",
"DateRangeJune",
"DateRangeJuly",
"DateRangeAugust",
"DateRangeSeptember",
"DateRangeOctober",
"DateRangeNovember",
"DateRangeDecember",
"DateRangePreviousYearThisMonth",
"DateRangePreviousYearLastMonth",
"DateRangePreviousYearNextMonth",
"TimeSpanDays",
"TimeSpanMonths",
"WorkOrderItemLaborServiceRateQuantity",
"Name",
"TimeSpan",
"Interval",
"WorkOrder",
"WorkOrderItem"
]);
}
/////////////////////////////////
//
//
function populateSelectionLists(vm) {
vm.selectLists.dateFilterTokens.push(
...[
// { name: vm.$ay.t("DateRangeYesterday"), id: "*yesterday*" },
// { name: vm.$ay.t("DateRangeToday"), id: "*today*" },
{ name: vm.$ay.t("DateRangeThisYear"), id: "*thisyear*" },
{ name: vm.$ay.t("DateRangeThisMonth"), id: "*thismonth*" },
{ name: vm.$ay.t("DateRangeThisWeek"), id: "*thisweek*" },
{ name: vm.$ay.t("DateRangeLastYear"), id: "*lastyear*" }, //prior year from jan to dec
{ name: vm.$ay.t("DateRangeLastMonth"), id: "*lastmonth*" },
{ name: vm.$ay.t("DateRangeLastWeek"), id: "*lastweek*" },
//-------------------------- rando ones -------------------
{ name: vm.$ay.t("DateRange14DayWindow"), id: "*14daywindow*" },
{ name: vm.$ay.t("DateRangePast"), id: "*past*" },
{
name: vm.$ay.t("DateRangeInTheLastThreeMonths"),
id: "*last3months*"
},
{
name: vm.$ay.t("DateRangeInTheLastSixMonths"),
id: "*last6months*"
},
{ name: vm.$ay.t("DateRangePastYear"), id: "*pastyear*" }, //last 365 days
{ name: vm.$ay.t("DateRangePast90Days"), id: "*past90days*" },
{ name: vm.$ay.t("DateRangePast30Days"), id: "*past30days*" },
{ name: vm.$ay.t("DateRangePast7Days"), id: "*past7days*" },
// { name: vm.$ay.t("DateRangePast24Hours"), id: "*past24hours*" },
// { name: vm.$ay.t("DateRangePast6Hours"), id: "*past6hours*" },
{ name: vm.$ay.t("DateRangeJanuary"), id: "*january*" },
{ name: vm.$ay.t("DateRangeFebruary"), id: "*february*" },
{ name: vm.$ay.t("DateRangeMarch"), id: "*march*" },
{ name: vm.$ay.t("DateRangeApril"), id: "*april*" },
{ name: vm.$ay.t("DateRangeMay"), id: "*may*" },
{ name: vm.$ay.t("DateRangeJune"), id: "*june*" },
{ name: vm.$ay.t("DateRangeJuly"), id: "*july*" },
{ name: vm.$ay.t("DateRangeAugust"), id: "*august*" },
{ name: vm.$ay.t("DateRangeSeptember"), id: "*september*" },
{ name: vm.$ay.t("DateRangeOctober"), id: "*october*" },
{ name: vm.$ay.t("DateRangeNovember"), id: "*november*" },
{ name: vm.$ay.t("DateRangeDecember"), id: "*december*" },
{
name: vm.$ay.t("DateRangePreviousYearThisMonth"),
id: "*lastyearthismonth*"
},
{
name: vm.$ay.t("DateRangePreviousYearLastMonth"),
id: "*lastyearlastmonth*"
},
{
name: vm.$ay.t("DateRangePreviousYearNextMonth"),
id: "*lastyearnextmonth*"
}
]
);
vm.selectLists.units.push(
...[
{ name: vm.$ay.t("TimeSpanDays"), id: "day" },
{
name: vm.$ay.t("TimeSpanMonths"),
id: "month"
}
]
);
}
</script>

View File

@@ -147,6 +147,7 @@ import {
faLayerGroup, faLayerGroup,
faLifeRing, faLifeRing,
faLink, faLink,
faListAlt,
faListOl, faListOl,
faListUl, faListUl,
faLock, faLock,
@@ -333,6 +334,7 @@ library.add(
faLayerGroup, faLayerGroup,
faLifeRing, faLifeRing,
faLink, faLink,
faListAlt,
faListOl, faListOl,
faListUl, faListUl,
faLock, faLock,
@@ -981,6 +983,12 @@ const CUSTOM_ICONS = {
icon: ["fas", "link"] icon: ["fas", "link"]
} }
}, },
ayiListAlt: {
component: FontAwesomeIcon,
props: {
icon: ["fas", "list-alt"]
}
},
ayiListOl: { ayiListOl: {
component: FontAwesomeIcon, component: FontAwesomeIcon,
props: { props: {