Files
raven-client/ayanova/src/views/svc-schedule.vue

1216 lines
38 KiB
Vue

<template>
<div v-if="formState.ready" v-resize="onResize" class="my-n8">
<gz-error :error-box-message="formState.errorBoxMessage"></gz-error>
<!-- {{ categories }}<br />
{{ events }} -->
<v-sheet height="64">
<v-toolbar flat class="ml-n3">
<v-btn outlined class="xxmr-4" color="grey darken-2" @click="setToday">
{{ $ay.t("DateRangeToday") }}
</v-btn>
<v-btn fab text small color="grey darken-2" @click="prev">
<v-icon small>$prev</v-icon>
</v-btn>
<v-btn fab text small color="grey darken-2" @click="next">
<v-icon small>$next</v-icon>
</v-btn>
<v-toolbar-title v-if="$refs.calendar">
<v-btn text @click="viewType = 'month'">
{{ $refs.calendar.title }}</v-btn
>
</v-toolbar-title>
<v-spacer v-if="!$vuetify.breakpoint.xs"></v-spacer>
<v-btn
class="mr-3"
fab
text
small
color="grey darken-2"
@click="settingsDialog = true"
>
<v-icon small>$ayiCog</v-icon>
</v-btn>
<v-menu bottom right>
<template v-slot:activator="{ on, attrs }">
<v-btn outlined color="grey darken-2" v-bind="attrs" v-on="on">
<span>{{ typeToLabel() }}</span>
<v-icon right>$sort</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item @click="viewType = 'category'">
<v-list-item-title>{{
$ay.t("ScheduleCategory")
}}</v-list-item-title>
</v-list-item>
<v-list-item @click="viewType = 'day'">
<v-list-item-title>{{ $ay.t("ScheduleDay") }}</v-list-item-title>
</v-list-item>
<v-list-item @click="viewType = 'week'">
<v-list-item-title>{{ $ay.t("ScheduleWeek") }}</v-list-item-title>
</v-list-item>
<v-list-item @click="viewType = 'month'">
<v-list-item-title>{{
$ay.t("ScheduleMonth")
}}</v-list-item-title>
</v-list-item>
<v-list-item @click="viewType = '4day'">
<v-list-item-title>{{ $ay.t("Schedule4Day") }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-toolbar>
</v-sheet>
<v-sheet :height="calendarHeight">
<v-calendar
ref="calendar"
v-model="focus"
color="primary"
:events="events"
:event-color="getEventColor"
:type="viewType"
:locale="languageName"
:event-more-text="$ay.t('More')"
:first-time="formUserOptions.firstTime"
:weekdays="weekdays"
@click:more="viewDay"
@click:date="viewDay"
@change="fetchEvents"
@mousedown:event="startDrag"
@mousedown:time="startTime"
@mousedown:time-category="startTime"
@mousemove:time="mouseMoveDayView"
@mousemove:time-category="mouseMoveDayView"
@mousemove:day="mouseMoveMonthView"
@mouseup:day="endDragExtend"
@mouseup:day-category="endDragExtend"
@mouseup:time="endDragExtend"
@mouseup:time-category="endDragExtend"
@mouseleave.native="cancelDrag"
category-show-all
:categories="categories"
category-text="name"
category-for-invalid="UNKNOWN USER"
>
<!-- @mousemove:day-category="mouseMoveMonthView" category-hide-dynamic -->
<template v-slot:event="{ event, timed, eventSummary }">
<div class="v-event-draggable">
<v-icon small :color="event.textColor" class="mr-1">{{
iconForEvent(event.type)
}}</v-icon>
<span
:class="event.textColor + '--text'"
v-html="eventSummary()"
/><v-icon
v-if="!event.editable"
x-small
:color="event.textColor"
class="ml-1"
>
$ayiLock</v-icon
>
</div>
<div
v-if="timed && event.editable"
class="v-event-drag-bottom"
@mousedown.stop="extendBottom(event)"
></div>
</template>
</v-calendar>
<!-- NEW ITEM DIALOG -->
<template>
<v-row justify="center">
<v-dialog max-width="360px" persistent v-model="newItemDialog">
<v-card>
<v-card-title>{{ $ay.t("New") }}</v-card-title>
<v-card-text>
<v-col cols="12">
<v-btn
x-large
block
@click="newItem($ay.ayt().WorkOrderItemScheduledUser)"
><v-icon large left>$ayiTools</v-icon
>{{ $ay.t("WorkOrder") }}</v-btn
> </v-col
><v-col cols="12">
<v-btn x-large block @click="newItem($ay.ayt().Reminder)"
><v-icon large left>$ayiStickyNote</v-icon
>{{ $ay.t("Reminder") }}</v-btn
>
</v-col>
</v-card-text>
<v-card-actions>
<v-btn text @click="cancelAddNew" color="primary">{{
$ay.t("Cancel")
}}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</template>
<!-- MORE INFO DIALOG -->
<template>
<v-row justify="center">
<v-dialog max-width="600px" v-model="moreInfoDialog">
<v-card>
<v-toolbar>
<v-btn icon @click="openScheduledItem()">
<v-icon color="primary">{{ iconForSelectedEvent() }}</v-icon>
</v-btn>
<v-toolbar-title>{{ selectedEvent.name }}</v-toolbar-title>
</v-toolbar>
<v-card-text>
<!--reminder -->
<template v-if="selectedEvent.type == $ay.ayt().Reminder">
<div>
<span class="text-h6">{{ $ay.t("ReminderName") }}:</span>
<span class="text-body-1 ml-2">{{ evInfo.name }}</span>
</div>
<div>
<span class="text-h6"
>{{ $ay.t("DashboardScheduled") }}:</span
>
<span class="text-body-1 ml-2"
>{{ $ay.dt(evInfo.startDate) }}&nbsp;&mdash;&nbsp;{{
$ay.dt(evInfo.stopDate)
}}</span
>
</div>
<div>
<span class="text-h6">{{ $ay.t("ReminderNotes") }}:</span>
<span class="text-body-1 ml-2">
<v-icon class="mr-3" :color="evInfo.color"
>$ayiSquareFull</v-icon
>{{ evInfo.notes }}</span
>
</div>
</template>
<!--review -->
<template v-if="selectedEvent.type == $ay.ayt().Review">
<div class="mb-1" v-if="evInfo.aType">
<v-icon
large
color="primary"
@click="openObject(evInfo.aType, evInfo.objectId)"
>{{ $ay.util().iconForType(evInfo.aType) }}</v-icon
><span
class="text-h6"
@click="openObject(evInfo.aType, evInfo.objectId)"
>
{{ evInfo.reviewObjectViz }}</span
>
</div>
<div>
<span class="text-h6">{{ $ay.t("ReviewName") }}:</span>
<span class="text-body-1 ml-2">{{ evInfo.name }}</span>
</div>
<div>
<span class="text-h6">{{ $ay.t("ReviewDate") }}:</span>
<span class="text-body-1 ml-2">{{
$ay.dt(evInfo.reviewDate)
}}</span>
</div>
<div>
<span class="text-h6">{{ $ay.t("ReviewNotes") }}:</span>
<span class="text-body-1 ml-2"> {{ evInfo.notes }}</span>
</div>
<template v-if="evInfo.completedDate">
<div>
<span class="text-h6"
>{{ $ay.t("ReviewCompletedDate") }}:</span
>
<span class="text-body-1 ml-2">{{
$ay.dt(evInfo.completedDate)
}}</span>
</div>
<div>
<span class="text-h6"
>{{ $ay.t("ReviewCompletionNotes") }}:</span
>
<span class="text-body-1 ml-2">
{{ evInfo.completionNotes }}</span
>
</div>
</template>
</template>
<!--woitemscheduleduser -->
<template
v-if="
selectedEvent.type == $ay.ayt().WorkOrderItemScheduledUser
"
>
<div>
<span class="text-h6">{{ $ay.t("WorkOrder") }}:</span>
<span class="text-body-1 ml-2"
>{{ evInfo.serial }}&nbsp; {{ evInfo.customerViz }}</span
>
</div>
<div>
<span class="text-h6">{{ $ay.t("Tags") }}:</span>
<span class="text-body-1 ml-2">{{
$ay.util().formatTags(evInfo.wotags)
}}</span>
</div>
<div>
<span class="text-h6"
>{{ $ay.t("DashboardScheduled") }}:</span
>
<span class="text-body-1 ml-2"
>{{ $ay.dt(evInfo.startDate) }}&nbsp;&mdash;&nbsp;{{
$ay.dt(evInfo.stopDate)
}}</span
>
</div>
<div>
<span class="text-h6"
>{{
$ay.t("WorkOrderItemScheduledUserEstimatedQuantity")
}}:</span
>
<span class="text-body-1 ml-2">{{ evInfo.qty }}</span>
</div>
<div>
<span class="text-h6"
>{{
$ay.t("WorkOrderItemScheduledUserServiceRateID")
}}:</span
>
<span class="text-body-1 ml-2">{{ evInfo.rate }}</span>
</div>
<div v-if="evInfo.haswostatus">
<span class="text-h6">{{ $ay.t("WorkOrderStatus") }}:</span>
<span class="text-body-1 ml-2">{{ evInfo.wostatus }}</span>
<v-icon :color="evInfo.wostatuscolor" class="ml-4"
>$ayiFlag</v-icon
>
<v-icon
color="primary"
v-if="evInfo.wostatuslocked"
class="ml-4"
>$ayiLock</v-icon
>
<v-icon
color="primary"
v-if="evInfo.wostatuscompleted"
class="ml-4"
>$ayiCheckCircle</v-icon
>
</div>
<div>
<span class="text-h6"
>{{ $ay.t("WorkOrderItemSummary") }}:</span
>
<span class="text-body-1 ml-2">
<v-icon class="mr-3" :color="evInfo.woitemstatuscolor"
>$ayiCircle</v-icon
>{{ evInfo.woitemstatus }}</span
>
</div>
<div>
<span class="text-h6"
>{{ $ay.t("WorkOrderItemPriorityID") }}:</span
>
<span class="text-body-1 ml-2">
<v-icon class="mr-3" :color="evInfo.woitemprioritycolor"
>$ayiFireAlt</v-icon
>{{ evInfo.woitempriority }}</span
>
</div>
<div>
<span class="text-h6"
>{{ $ay.t("WorkOrderItemTags") }}:</span
>
<span class="text-body-1 ml-2">{{
$ay.util().formatTags(evInfo.woitemtags)
}}</span>
</div>
</template>
</v-card-text>
<v-card-actions>
<v-btn color="primary" text @click="openScheduledItem()">{{
$ay.t("Open")
}}</v-btn>
<v-spacer v-if="!$vuetify.breakpoint.xs"></v-spacer>
<v-btn color="primary" text @click="moreInfoDialog = false">
{{ $ay.t("Close") }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</template>
</v-sheet>
<template>
<!-- ############## SETTINGS DIALOG #################-->
<v-row justify="center">
<v-dialog max-width="600px" v-model="settingsDialog">
<v-card>
<v-card-title>{{ $ay.t("ScheduleOptions") }} </v-card-title>
<v-card-text>
<v-row no-gutters>
<v-col cols="12" class="mt-2">
<gz-time-picker
:label="$ay.t('ScheduleFirstHour')"
v-model="tempFirstTime"
></gz-time-picker>
</v-col>
<v-col cols="12">
<GZDaysOfWeek
:label="$ay.t('ExcludeDaysOfWeek')"
v-model="formUserOptions.excludeDaysOfWeek"
ref="daysofweek"
></GZDaysOfWeek>
</v-col>
<v-col cols="12">
<v-radio-group
v-model="formUserOptions.wisuColorSource"
:mandatory="true"
:label="$ay.t('ScheduleWOColorFrom')"
class="ml-3 mt-n2"
>
<v-radio :label="$ay.t('NoColor')" value="0"></v-radio>
<v-radio
:label="$ay.t('WorkOrderStatus')"
value="2"
></v-radio>
<v-radio
:label="$ay.t('WorkOrderItemWorkOrderStatusID')"
value="3"
></v-radio>
<v-radio
:label="$ay.t('WorkOrderItemPriorityID')"
value="4"
></v-radio>
</v-radio-group>
</v-col>
</v-row>
</v-card-text>
<v-card-actions>
<v-btn text @click="settingsDialog = false" color="primary">{{
$ay.t("Cancel")
}}</v-btn>
<v-spacer v-if="!$vuetify.breakpoint.xs"></v-spacer>
<v-btn
color="primary"
text
@click="saveUserOptions()"
class="ml-4"
>{{ $ay.t("Save") }}</v-btn
>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</template>
</div>
</template>
<script>
const FORM_KEY = "svc-schedule";
import GZDaysOfWeek from "../components/days-of-week-control.vue";
export default {
components: {
GZDaysOfWeek
},
async created() {
const vm = this;
try {
await initForm(vm);
window.$gz.eventBus.$on("menu-click", clickHandler);
//----------------------------
generateMenu(vm);
} catch (error) {
window.$gz.errorHandler.handleFormError(error, vm);
} finally {
vm.formState.ready = true;
}
},
beforeDestroy() {
saveFormSettings(this);
window.$gz.eventBus.$off("menu-click", clickHandler);
},
data() {
return {
focus: "",
viewType: "month",
selectedEvent: {},
selectedElement: null,
events: [],
categories: [],
evInfo: {},
dragEvent: null,
extendEvent: null,
createStart: null,
extendOriginal: null,
dragged: false,
dragTimeout: null,
formState: {
ready: false,
dirty: false,
valid: true,
readOnly: false,
loading: true,
errorBoxMessage: null,
appError: null,
serverError: {}
},
calendarHeight: 600,
settingsDialog: false,
newItemDialog: false,
moreInfoDialog: false,
timeZoneName: window.$gz.locale.getResolvedTimeZoneName(),
languageName: window.$gz.locale.getResolvedLanguage(),
hour12: window.$gz.locale.getHour12(),
formUserOptions: {},
tempFirstTime: null,
availableUsers: [],
lastMouseDownMS: null
};
},
methods: {
openObject: function(type, id) {
window.$gz.eventBus.$emit("openobject", {
type: type,
id: id
});
},
newItem(atype) {
const newEvent = this.events[this.events.length - 1];
const addStart = window.$gz.locale.localScheduleFormatToUTC8601String(
newEvent.start,
this.timeZoneName
);
const addEnd = window.$gz.locale.localScheduleFormatToUTC8601String(
newEvent.end,
this.timeZoneName
);
switch (atype) {
case this.$ay.ayt().WorkOrderItemScheduledUser:
this.$router.push({
name: "workorder-edit",
params: {
recordid: 0,
add: {
type: atype,
start: addStart,
end: addEnd,
userId: this.$store.state.userId,
name: this.$store.state.userName
}
}
});
break;
case this.$ay.ayt().Reminder:
this.$router.push({
name: "reminder-edit",
params: {
recordid: 0,
add: {
start: addStart,
end: addEnd,
userId: this.$store.state.userId,
name: this.$store.state.userName
}
}
});
break;
}
//remove faux item, server will provide it back once it's created anyway
this.events.splice(this.events.length - 1);
this.newItemDialog = false;
},
cancelAddNew() {
this.events.splice(this.events.length - 1);
this.newItemDialog = false;
},
extendBottom(event) {
if (event.editable) {
//capture time to see if it's a click or a drag/extend
this.lastMouseDownMS = new Date().getTime();
this.extendEvent = event;
this.createStart = event.start;
this.extendOriginal = event.end;
}
},
async endDragExtend(e) {
//Vuetify bug in calendar apparently where it fires for both day and day-category views
//since I set them to both call into this same method I need to ignore the incorrect one
if (!e.category && this.viewType == "category") {
return;
}
//On drag then dragged is set to true and dragEvent and dragTime are set
//on extend then dragged is set to true extendEvent (actual event), extendOriginal and createStart are set, dragEvent is null
//on create then dragged is set to false and createStart is only value set, dragEvent is null and extendEvent is null
//Handle the event, could be one of three things: changing an event start time, changing an event length or creating a new event
if (this.extendEvent && this.extendEvent.type == 0) {
//NEW, prompt for deets and create or if cancelled then just remove this faux event from events list (it will be the last one in the array)
this.newItemDialog = true;
} else {
//MORE INFO
if (this.lastMouseDownMS != null && this.itWasAClickNotADrag()) {
await this.showMoreInfo(this.dragEvent);
this.dragEvent = null; //this needs to be set or it will keep dragging off an editable event even as the moreinfo dialog show
return;
}
//MODIFY existing event, drag or extend
if (this.dragEvent || this.extendEvent) {
const param = { type: null, id: null, start: null, end: null };
if (this.dragEvent) {
param.type = this.dragEvent.type;
param.id = this.dragEvent.id;
param.start = window.$gz.locale.localScheduleFormatToUTC8601String(
this.dragEvent.start,
this.timeZoneName
);
param.end = window.$gz.locale.localScheduleFormatToUTC8601String(
this.dragEvent.end,
this.timeZoneName
);
} else {
param.type = this.extendEvent.type;
param.id = this.extendEvent.id;
param.start = window.$gz.locale.localScheduleFormatToUTC8601String(
this.extendEvent.start,
this.timeZoneName
);
param.end = window.$gz.locale.localScheduleFormatToUTC8601String(
this.extendEvent.end,
this.timeZoneName
);
}
try {
window.$gz.form.deleteAllErrorBoxErrors(this);
const res = await window.$gz.api.post("schedule/adjust", param);
if (res.error) {
this.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(this);
} else {
}
} catch (error) {
window.$gz.errorHandler.handleFormError(error, this);
}
}
}
this.dragTime = null;
this.dragEvent = null;
this.extendEvent = null;
this.createStart = null;
this.extendOriginal = null;
},
cancelDrag() {
if (this.extendEvent) {
if (this.extendOriginal) {
this.extendEvent.end = this.extendOriginal;
} else {
const i = this.events.indexOf(this.extendEvent);
if (i !== -1) {
this.events.splice(i, 1);
}
}
}
this.extendEvent = null;
this.createStart = null;
this.dragTime = null;
this.dragEvent = null;
this.lastMouseDownMS = null;
},
startDrag({ event }) {
//# mouse down on an event triggers this call
if (event) {
this.lastMouseDownMS = new Date().getTime(); //snapshot time to disambiguate drag vs click
if (event.editable) {
this.dragged = false;
this.dragEvent = event;
this.dragTime = null;
this.extendOriginal = null;
} else {
this.dragged = false;
this.dragEvent = event;
this.dragTime = null;
}
}
},
itWasAClickNotADrag() {
if (this.lastMouseDownMS == null) {
if (this.$ay.dev) {
throw new Error("lastMouseDownMS is null!");
} else {
window.$gz.store.commit(
"logItem",
"home-schedule:lastMouseDownMS is unexpectedly null"
);
return true; //least dangerous option in production
}
}
const elapsed = new Date().getTime() - this.lastMouseDownMS;
this.lastMouseDownMS = null;
return elapsed < 200;
},
mouseMoveDayView(tms) {
//no event being dragged or exgtended?
if (!this.dragEvent && !this.extendEvent) {
return;
}
const mouse = this.toTime(tms);
if (this.dragEvent && this.dragTime !== null) {
//# DRAGGING PATH
const start = this.dragEvent.start;
const end = this.dragEvent.end;
const duration = end - start;
const newStartTime = mouse - this.dragTime;
const newStart = this.roundTime(newStartTime);
const newEnd = newStart + duration;
this.dragEvent.start = newStart;
this.dragEvent.end = newEnd;
} else if (this.extendEvent && this.createStart !== null) {
//# EXTENDING PATH
const mouseRounded = this.roundTime(mouse, false);
const min = Math.min(mouseRounded, this.createStart);
const max = Math.max(mouseRounded, this.createStart);
this.extendEvent.start = min;
this.extendEvent.end = max;
}
},
mouseMoveMonthView(dd) {
if (!this.dragEvent) {
return;
}
//# DRAGGING PATH MONTH VIEW
//need to get the actual start time as it isn't in the mouse date
const dragEventStartDate = new Date(this.dragEvent.start);
const mouseDate = new Date(
dd.year,
dd.month - 1,
dd.day,
dragEventStartDate.getHours(),
dragEventStartDate.getMinutes()
).getTime();
const moveDelta = mouseDate - this.dragEvent.start;
this.dragEvent.start = mouseDate;
this.dragEvent.end = this.dragEvent.end + moveDelta;
},
startTime(tms) {
//due to vuetify calendar bug day and day-category both fire to this event handler in day-category view so need to reject incorrect one here
if (!tms.category && this.viewType == "category") {
return;
}
//This is called on the start of dragging an existing schedule item or drag extending a NEW schedule item
const mouse = this.toTime(tms);
if (this.dragEvent && this.dragTime === null) {
//# DAY VIEW *DRAG* EXISTING START EVENT (not extend)
//(also called on simple click to view schedule more info)
if (this.dragEvent.editable) {
const start = this.dragEvent.start;
this.dragTime = mouse - start;
}
} else {
//# DAY VIEW CREATE START EVENT
this.createStart = this.roundTime(mouse);
this.extendEvent = {
name: "-",
color: this.$store.state.darkMode ? "white" : "black",
textColor: this.$store.state.darkMode ? "black" : "white",
start: this.createStart,
end: this.createStart,
timed: true,
type: 0,
id: 0,
editable: true
};
this.events.push(this.extendEvent);
}
},
roundTime(time, down = true) {
const roundTo = 15; // minutes
const roundDownTime = roundTo * 60 * 1000;
return down
? time - (time % roundDownTime)
: time + (roundDownTime - (time % roundDownTime));
},
toTime(tms) {
return new Date(
tms.year,
tms.month - 1,
tms.day,
tms.hour,
tms.minute
).getTime();
},
typeToLabel() {
switch (this.viewType) {
case "month":
return this.$ay.t("ScheduleMonth");
case "week":
return this.$ay.t("ScheduleWeek");
case "day":
return this.$ay.t("ScheduleDay");
case "4day":
return this.$ay.t("Schedule4Day");
case "category":
return this.$ay.t("ScheduleCategory");
}
},
onResize() {
this.calendarHeight = window.innerHeight * 0.84;
},
viewDay({ date }) {
this.focus = date;
this.viewType = "category";
},
getEventColor(event) {
return event.color;
},
setToday() {
this.focus = "";
},
prev() {
this.$refs.calendar.prev();
},
next() {
this.$refs.calendar.next();
},
iconForSelectedEvent() {
return window.$gz.util.iconForType(this.selectedEvent.type);
},
iconForEvent(type) {
return window.$gz.util.iconForType(type);
},
openScheduledItem() {
window.$gz.eventBus.$emit("openobject", {
type: this.selectedEvent.type,
id: this.selectedEvent.id
});
},
async showMoreInfo(event) {
this.selectedEvent = event;
let route = null;
this.evInfo = {};
switch (event.type) {
case window.$gz.type.WorkOrderItemScheduledUser:
route = `workorder/items/scheduled-users/sched-info/${event.id}`;
break;
case window.$gz.type.Reminder:
route = `reminder/sched-info/${event.id}`;
break;
case window.$gz.type.Review:
route = `review/sched-info/${event.id}`;
break;
}
if (route) {
const res = await window.$gz.api.get(route);
if (!res.error) {
this.evInfo = res.data;
this.moreInfoDialog = true;
} else {
this.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(this);
}
}
},
async fetchEvents({ start, end }) {
try {
window.$gz.form.deleteAllErrorBoxErrors(this);
const res = await window.$gz.api.post("schedule/svc", {
view: window.$gz.util.calendarViewToAyaNovaEnum(this.viewType),
dark: this.$store.state.darkMode,
start: window.$gz.locale.localTimeDateStringToUTC8601String(
`${start.date}T00:00:00`,
this.timeZoneName
),
end: window.$gz.locale.localTimeDateStringToUTC8601String(
`${end.date}T23:59:59`,
this.timeZoneName
),
wisuColorSource: this.formUserOptions.wisuColorSource,
wisu: this.formUserOptions.wisu,
reviews: this.formUserOptions.reviews,
reminders: this.formUserOptions.reminders,
users: this.formUserOptions.users
});
if (res.error) {
this.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(this);
} else {
this.events.splice(0);
const timeZoneName = this.timeZoneName;
let i = res.data.length;
while (i--) {
const x = res.data[i];
this.events.push({
start: new Date(
new Date(x.start)
.toLocaleString("sv-SE", {
timeZone: timeZoneName
})
.replace(" ", "T")
).getTime(),
end: new Date(
new Date(x.end)
.toLocaleString("sv-SE", {
timeZone: timeZoneName
})
.replace(" ", "T")
).getTime(),
timed: true,
name: x.name,
color: x.color,
textColor: x.textColor,
type: x.type,
id: x.id,
editable: x.editable,
userId: x.userId,
category: this.availableUsers.find(a => a.id == x.userId).name
});
}
}
} catch (error) {
window.$gz.errorHandler.handleFormError(error, this);
}
},
async saveUserOptions() {
this.settingsDialog = false;
await saveFormUserOptions(this);
//doesn't seem to be any reliable way to trigger refresh on the calendar itself
//and trying to load the events manually is not working out as the calendar reports a different
//start and end than it provides for the change event
//this is brutal but works
window.location.reload();
}
//eom
},
computed: {
weekdays() {
return window.$gz.util.DaysOfWeekToWeekdays(
this.formUserOptions.excludeDaysOfWeek
);
}
}
};
/////////////////////////////
//
//
async function clickHandler(menuItem) {
if (!menuItem) {
return;
}
const m = window.$gz.menu.parseMenuItem(menuItem);
if (m.owner == FORM_KEY && !m.disabled) {
switch (m.key) {
// case "report":
// const res = await m.vm.$refs.reportSelector.open(
// {
// AType: window.$gz.type.Project,
// selectedRowIds: [m.vm.obj.id]
// },
// m.id
// );
// if (res == null) {
// return;
// }
// window.$gz.form.setLastReport(FORM_KEY, res);
// generateMenu(m.vm);
// break;
case "WorkOrderItemScheduledUserList":
m.vm.$router.push({
name: "svc-workorder-item-scheduled-users",
params: {
aType: window.$gz.type.User,
objectId: m.vm.$store.state.userId,
name: m.vm.$store.state.userName
}
});
break;
case "WorkOrderItemLaborList":
m.vm.$router.push({
name: "svc-workorder-item-labors",
params: {
aType: window.$gz.type.User,
objectId: m.vm.$store.state.userId,
name: m.vm.$store.state.userName
}
});
break;
case "ReminderList":
m.vm.$router.push({
name: "home-reminders"
});
break;
case "ReviewList":
m.vm.$router.push({
name: "home-reviews"
});
break;
default:
window.$gz.eventBus.$emit(
"notify-warning",
FORM_KEY + "::context click: [" + m.key + "]"
);
}
}
}
//////////////////////
//
//
function generateMenu(vm) {
const menuOptions = {
isMain: true,
readOnly: vm.formState.readOnly,
icon: "$ayiCalendarAlt",
title: "Schedule",
helpUrl: "svc-schedule",
menuItems: []
};
// //
// menuOptions.menuItems.push({
// title: "Report",
// icon: "$ayiFileAlt",
// key: FORM_KEY + ":report",
// vm: vm
// });
//
// const lastReport = window.$gz.form.getLastReport(FORM_KEY);
// if (lastReport != null) {
// menuOptions.menuItems.push({
// title: lastReport.name,
// notrans: true,
// icon: "$ayiFileAlt",
// key: FORM_KEY + ":report:" + lastReport.id,
// vm: vm
// });
// }
menuOptions.menuItems.push({
title: "ReminderList",
icon: "$ayiStickyNote",
key: FORM_KEY + ":ReminderList",
vm: vm
});
menuOptions.menuItems.push({
title: "ReviewList",
icon: "$ayiCalendarCheck",
key: FORM_KEY + ":ReviewList",
vm: vm
});
if (vm.$store.getters.isScheduleableUser) {
menuOptions.menuItems.push({ divider: true, inset: false });
menuOptions.menuItems.push({
title: "WorkOrderItemScheduledUserList",
icon: "$ayiUserClock",
key: FORM_KEY + ":WorkOrderItemScheduledUserList",
vm: vm
});
menuOptions.menuItems.push({
title: "WorkOrderItemLaborList",
icon: "$ayiHammer",
key: FORM_KEY + ":WorkOrderItemLaborList",
vm: vm
});
}
menuOptions.menuItems.push({ divider: true, inset: false });
window.$gz.eventBus.$emit("menu-change", menuOptions);
}
/////////////////////////////////
//
//
async function initForm(vm) {
await fetchTranslatedText(vm);
await fetchAvailableUsers(vm);
getFormSettings(vm);
await getFormUserOptions(vm);
}
//////////////////////
//
//
async function fetchAvailableUsers(vm) {
const res = await window.$gz.api.get("schedule/scheduleable-user-list");
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
vm.availableUsers = res.data;
}
}
function getFormSettings(vm) {
let formSettings = window.$gz.form.getFormSettings(FORM_KEY);
if (!formSettings || !formSettings.temp || !formSettings.temp.viewType) {
//defaults
formSettings = { temp: { viewType: "month", focus: null } };
}
vm.viewType = formSettings.temp.viewType;
vm.focus = formSettings.temp.focus;
return formSettings;
}
function saveFormSettings(vm) {
const formSettings = window.$gz.form.getFormSettings(FORM_KEY);
formSettings.temp = { viewType: vm.viewType, focus: vm.focus };
window.$gz.form.setFormSettings(FORM_KEY, formSettings);
}
////////////////////
//
async function getFormUserOptions(vm) {
const res = await window.$gz.api.get(`form-user-options/${FORM_KEY}`);
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
if (res.data == null) {
//make a default
vm.formUserOptions = {
firstTime: "00:00",
excludeDaysOfWeek: 0,
wisuColorSource: "2",
wisu: true,
users: [...vm.availableUsers.map(x => x.id)] //default to all users
};
} else {
vm.formUserOptions = JSON.parse(res.data.options);
}
vm.categories = vm.formUserOptions.users.map(x => {
return vm.availableUsers.find(a => a.id == x);
});
//takes local time in "HH:MM" format and converts to ISO UTC format for picker consumption
const d = new Date();
const temp = new Date(
d.getFullYear(),
d.getMonth(),
d.getDate(),
vm.formUserOptions.firstTime.split(":")[0],
vm.formUserOptions.firstTime.split(":")[1]
);
vm.tempFirstTime = window.$gz.locale.localTimeDateStringToUTC8601String(
temp.toISOString(),
vm.timeZoneName
);
}
}
////////////////////
//
async function saveFormUserOptions(vm) {
//translate tempFirstTime to local time
if (vm.tempFirstTime) {
//convert picker's utc time to local hour:MM
const localTime = window.$gz.DateTime.fromISO(vm.tempFirstTime).toLocal();
vm.formUserOptions.firstTime = `${localTime.hour}:${localTime.minute}`;
}
const res = await window.$gz.api.post("form-user-options", {
formKey: FORM_KEY,
options: JSON.stringify(vm.formUserOptions)
});
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
}
}
//////////////////////////////////////////////////////////
//
// Ensures UI translated text is available
//
async function fetchTranslatedText() {
await window.$gz.translation.cacheTranslations([
"DateRangeToday",
"ExcludeDaysOfWeek",
"ScheduleMonth",
"ScheduleDay",
"ScheduleWeek",
"Schedule4Day",
"ScheduleFirstHour",
"ScheduleWOColorFrom",
"ScheduleOptions",
"ScheduleShowTypes",
"NoColor",
"WorkOrder",
"Reminder",
"WorkOrderList",
"ReminderList",
"ReviewList",
"DashboardScheduled",
"WorkOrderItemPriorityID",
"WorkOrderItemSummary",
"WorkOrderItemWorkOrderStatusID",
"WorkOrderStatus",
"WorkOrderItemScheduledUserEstimatedQuantity",
"WorkOrderItemScheduledUserServiceRateID",
"WorkOrderItemTags",
"ReminderName",
"ReviewDate",
"ReminderNotes",
"ReviewName",
"ReviewNotes",
"ReviewCompletedDate",
"ReviewCompletionNotes"
]);
}
</script>
<style scoped lang="scss">
.v-event-draggable {
padding-left: 6px;
}
.v-event-timed {
user-select: none;
-webkit-user-select: none;
}
.v-event-drag-bottom {
position: absolute;
left: 0;
right: 0;
bottom: 4px;
height: 4px;
cursor: ns-resize;
&::after {
display: none;
position: absolute;
left: 50%;
height: 4px;
border-top: 1px solid gray;
border-bottom: 1px solid gray;
width: 32px;
margin-left: -8px;
// opacity: 0.8;
content: "";
}
&:hover::after {
display: block;
}
}
</style>