client/src/MobicoopBundle/Resources/assets/js/components/carpool/publish/AdPlanification.vue
<template>
<v-container fluid>
<v-form v-if="!regular">
<!-- Punctual -->
<!-- First row -->
<v-row
align="center"
justify="center"
dense
>
<!-- Outward date -->
<v-col
cols="5"
offset="2"
>
<v-menu
v-model="menuOutwardDate"
:close-on-content-click="false"
transition="scale-transition"
offset-y
min-width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
:value="computedOutwardDateFormat"
:label="$t('outwardDate.label')"
readonly
clearable
v-on="on"
@click:clear="clearOutwardDate"
>
<v-icon
slot="prepend"
>
mdi-arrow-right-circle-outline
</v-icon>
</v-text-field>
</template>
<v-date-picker
v-model="outwardDate"
:locale="locale"
no-title
:min="nowDate"
first-day-of-week="1"
@input="menuOutwardDate = false"
@change="changeDate()"
/>
</v-menu>
</v-col>
<!-- Outward time -->
<v-col
cols="4"
>
<!-- Digital clock -->
<v-text-field
v-if="defaultDigitalClock"
v-model="outwardTime"
:label="$t('outwardTime.label')"
prepend-icon="mdi-clock-time-eight-outline"
type="time"
@change="changeTime()"
/>
<!-- Needle clock -->
<v-menu
v-else
ref="menuOutwardTime"
v-model="menuOutwardTime"
:close-on-content-click="false"
transition="scale-transition"
offset-y
max-width="290px"
min-width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
id="outwardTime"
v-model="outwardTime"
:label="$t('outwardTime.label')"
prepend-icon=""
readonly
clearable
v-on="on"
@click:clear="clearOutwardTime"
/>
</template>
<v-time-picker
v-if="menuOutwardTime"
v-model="outwardTime"
format="24hr"
:min="maxTimeIfToday"
header-color="secondary"
:allowed-minutes="allowedStep"
@click:minute="changeTime()"
/>
</v-menu>
</v-col>
<v-col
cols="1"
>
<v-tooltip
color="info"
right
>
<template v-slot:activator="{ on }">
<v-icon
v-on="on"
>
mdi-help-circle-outline
</v-icon>
</template>
<span>
{{ infoMarginDuration }}</span>
</v-tooltip>
</v-col>
</v-row>
<!-- Second row -->
<v-row
align="center"
dense
>
<!-- Return trip ? -->
<v-col
cols="2"
>
<v-checkbox
v-model="returnTrip"
class="mt-0"
:label="$t('returnTrip.label')"
color="primary"
hide-details
@change="checkReturnDesactivate($event)"
/>
</v-col>
<!-- Return date -->
<v-col
cols="5"
>
<v-menu
v-model="menuReturnDate"
:close-on-content-click="false"
transition="scale-transition"
offset-y
min-width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
:value="computedReturnDateFormat"
:label="$t('returnDate.label')"
prepend-icon=""
readonly
clearable
v-on="on"
@click:clear="clearReturnDate"
>
<v-icon
slot="prepend"
>
mdi-arrow-left-circle-outline
</v-icon>
</v-text-field>
</template>
<v-date-picker
v-model="returnDate"
:locale="locale"
no-title
first-day-of-week="1"
:min="outwardDate"
@input="menuReturnDate = false"
@change="checkDateReturn($event)"
/>
</v-menu>
</v-col>
<!-- Return time -->
<v-col
cols="4"
>
<!-- Digital clock -->
<v-text-field
v-if="defaultDigitalClock"
v-model="returnTime"
:label="$t('returnTime.label')"
:min="minReturnTime"
prepend-icon="mdi-clock-time-eight-outline"
type="time"
@change="checkDateReturn($event)"
/>
<!-- Needle clock -->
<v-menu
v-else
ref="menuReturnTime"
v-model="menuReturnTime"
:close-on-content-click="false"
:return-value.sync="returnTime"
transition="scale-transition"
offset-y
max-width="290px"
min-width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="returnTime"
:label="$t('returnTime.label')"
prepend-icon=""
readonly
clearable
v-on="on"
@click:clear="clearReturnTime"
/>
</template>
<v-time-picker
v-if="menuReturnTime"
v-model="returnTime"
format="24hr"
header-color="secondary"
:min="minReturnTime"
:allowed-minutes="allowedStep"
@click:minute="checkDateReturn($event)"
/>
</v-menu>
</v-col>
<v-col
cols="1"
>
<v-tooltip
color="info"
right
>
<template v-slot:activator="{ on }">
<v-icon
v-on="on"
>
mdi-help-circle-outline
</v-icon>
</template>
<span> {{ infoMarginDuration }}
</span>
</v-tooltip>
</v-col>
</v-row>
<v-row v-if="returnTimeIsValid == false">
<v-col>
<p class="error--text">
{{ $t('errorReturnTime') }}
</p>
</v-col>
</v-row>
</v-form>
<!-- Regular -->
<v-form v-else>
<!-- we have a maximum of 7 different schedules, we iterate on them -->
<v-row
v-for="item in activeSchedules"
:key="item.id"
align="center"
justify="center"
>
<v-col
cols="10"
>
<!-- Schedule -->
<v-card>
<!-- Checkboxes for the days -->
<v-row
align="center"
justify="space-around"
dense
>
<v-checkbox
v-model="item.mon"
label="L"
color="primary"
:disabled="false"
@change="getValueCheckbox($event,item,'mon')"
/>
<v-checkbox
v-model="item.tue"
label="Ma"
color="primary"
@change="getValueCheckbox($event,item,'tue')"
/>
<v-checkbox
v-model="item.wed"
label="Me"
color="primary"
@change="getValueCheckbox($event,item,'wed')"
/>
<v-checkbox
v-model="item.thu"
label="J"
color="primary"
@change="getValueCheckbox($event,item,'thu')"
/>
<v-checkbox
v-model="item.fri"
label="V"
color="primary"
@change="getValueCheckbox($event,item,'fri')"
/>
<v-checkbox
v-model="item.sat"
label="S"
color="primary"
@change="getValueCheckbox($event,item,'sat')"
/>
<v-checkbox
v-model="item.sun"
label="D"
color="primary"
@change="getValueCheckbox($event,item,'sun')"
/>
</v-row>
<!-- Times -->
<v-row
align="center"
justify="space-around"
dense
>
<!-- Outward time -->
<v-col
cols="5"
>
<!-- Digital clock -->
<v-text-field
v-if="defaultDigitalClock"
v-model="item.outwardTime"
:label="$t('regularOutwardTime.label')"
class="ml-3"
prepend-icon="mdi-clock-time-eight-outline"
type="time"
@change="change"
/>
<!-- Needle clock -->
<v-menu
v-else
v-model="item.menuOutwardTime"
:close-on-content-click="false"
transition="scale-transition"
offset-y
max-width="290px"
min-width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="item.outwardTime"
:hint="item.id > 0 && staggeredSchedulesAllowed ? $t('optional') : ''"
persistent-hint
:label="$t('regularOutwardTime.label')"
prepend-icon=""
readonly
v-on="on"
@blur="change"
>
<v-icon
slot="prepend"
>
mdi-arrow-right-circle
</v-icon>
</v-text-field>
</template>
<!--
we can't use $refs with v-for : https://vuejs.org/v2/guide/components-edge-cases.html#Accessing-Child-Component-Instances-amp-Child-Elements
because $refs are not reactive, we have to use a custom method closeOutwardTime() which will close the menu
-->
<v-time-picker
v-if="item.menuOutwardTime"
v-model="item.outwardTime"
format="24hr"
header-color="secondary"
:disabled="item.outwardDisabled"
:allowed-minutes="allowedStep"
@click:minute="closeOutwardTime(item.id)"
@change="blockTimeRegular($event,item.id)"
/>
</v-menu>
</v-col>
<v-col
cols="1"
>
<v-tooltip
color="info"
right
>
<template v-slot:activator="{ on }">
<v-icon
v-on="on"
>
mdi-help-circle-outline
</v-icon>
</template>
<span>{{ infoMarginDuration }}
</span>
</v-tooltip>
</v-col>
<!-- Return time -->
<v-col
cols="5"
>
<!-- Digital clock -->
<v-text-field
v-if="defaultDigitalClock"
v-model="item.returnTime"
:label="$t('regularReturnTime.label')"
prepend-icon="mdi-clock-time-eight-outline"
type="time"
@change="change"
/>
<!-- Needle clock -->
<v-menu
v-else
v-model="item.menuReturnTime"
:close-on-content-click="false"
transition="scale-transition"
offset-y
max-width="290px"
min-width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="item.returnTime"
:label="$t('regularReturnTime.label')"
:hint="$t('optional')"
clearable
persistent-hint
prepend-icon=""
readonly
v-on="on"
@blur="change"
>
<v-icon
slot="prepend"
>
mdi-arrow-left-circle
</v-icon>
</v-text-field>
</template>
<!--
we can't use $refs with v-for : https://vuejs.org/v2/guide/components-edge-cases.html#Accessing-Child-Component-Instances-amp-Child-Elements
because $refs are not reactive, we have to use a custom method close() which will close the menu
-->
<v-time-picker
v-if="item.menuReturnTime"
v-model="item.returnTime"
format="24hr"
header-color="secondary"
:disabled="item.returnDisabled"
:min="item.minReturnTime ? item.minReturnTime : null"
:allowed-minutes="allowedStep"
@click:minute="closeReturnTime(item.id)"
@change="change()"
/>
</v-menu>
</v-col>
<v-col
cols="1"
>
<v-tooltip
color="info"
right
>
<template v-slot:activator="{ on }">
<v-icon
v-on="on"
>
mdi-help-circle-outline
</v-icon>
</template>
<span>{{ infoMarginDuration }}
</span>
</v-tooltip>
</v-col>
</v-row>
<!-- Remove schedule -->
<v-row
align="center"
justify="end"
dense
>
<v-col
cols="2"
>
<v-btn
v-if="item.id>0"
text
icon
@click="removeSchedule(item.id)"
>
<v-icon>
mdi-delete-circle
</v-icon>
</v-btn>
</v-col>
</v-row>
</v-card>
</v-col>
</v-row>
<!-- Add schedule -->
<v-row
v-if="!schedules[6].visible"
align="center"
justify="center"
dense
>
<v-col
cols="10"
>
<v-btn
text
icon
:disabled="!checkIfCurrentScheduleOk"
@click="addSchedule"
>
<v-icon>
mdi-plus-circle-outline
</v-icon>
{{ $t('addSchedule') }}
</v-btn>
</v-col>
</v-row>
</v-form>
</v-container>
</template>
<script>
import moment from "moment";
import { isEmpty, remove, clone } from "lodash";
import {messages_en, messages_fr, messages_eu, messages_nl} from "@translations/components/carpool/publish/AdPlanification/";
export default {
i18n: {
messages: {
'en': messages_en,
'nl': messages_nl,
'fr': messages_fr,
'eu':messages_eu
}
},
components: {
},
props: {
staggeredSchedulesAllowed: {
type: Boolean,
default: false
},
regular: {
type: Boolean,
default: false
},
initOutwardDate: {
type: String,
default: null
},
initOutwardTime: {
type: String,
default: null
},
initReturnDate: {
type: String,
default: null
},
initReturnTime: {
type: String,
default: null
},
defaultMarginDuration: {
type: Number,
default: null
},
route: {
type: Object,
default: null
},
initSchedule: {
type: Array,
default: null
},
defaultTimePrecision: {
type: Number,
default: null
},
defaultDigitalClock: {
type: Boolean,
default: false
}
},
data() {
return {
outwardDate: this.initOutwardDate,
outwardTime: moment(this.initOutwardTime).isValid() ? moment(this.initOutwardTime).format('HH:mm') : null,
returnDate: this.initReturnDate,
returnTime: moment(this.initReturnTime).isValid() ? moment(this.initReturnTime).format('HH:mm') : null,
menuOutwardDate: false,
menuOutwardTime: false,
menuReturnDate: false,
menuReturnTime: false,
returnTrip: !!(this.initReturnDate && this.initReturnTime),
marginDuration: this.defaultMarginDuration,
locale: localStorage.getItem("X-LOCALE"),
arrayDay : ['mon','tue','wed','thu','fri','sat','sun'],
schedules: [],
maxDateFromOutward : null,
maxTimeFromOutward : null,
maxTimeIfToday : null,
nowDate : new Date().toISOString().slice(0,10),
timePrecision: this.defaultTimePrecision,
};
},
computed: {
computedOutwardDateFormat() {
return this.outwardDate
? moment.utc(this.outwardDate).format(this.$t("fullDate"))
: "";
},
computedReturnDateFormat() {
return this.returnDate
? moment.utc(this.returnDate).format(this.$t("fullDate"))
: "";
},
activeSchedules() {
return this.schedules.filter(function(schedule) {
return schedule.visible;
});
},
infoMarginDuration() {
return this.$t("marginTooltip",{margin: this.marginDuration/60})
},
checkIfCurrentScheduleOk(){
for (var s in this.activeSchedules){
var i = this.activeSchedules[s];
if ( !i.mon && !i.tue && !i.wed && !i.thu && !i.fri && !i.sat && !i.sun ) {
return false;
}
if ( i.outwardTime == null && i.returnTime == null) return false;
}
return true;
},
returnTimeIsValid (){
return moment(this.returnDate + ' ' + this.returnTime).isValid() && moment(this.outwardDate + ' ' + this.outwardTime) && !isEmpty(this.route)
? moment(this.returnDate + ' ' + this.returnTime) >= moment(this.outwardDate + ' ' + this.outwardTime).add(this.route.direction.duration, 'seconds')
: null;
},
minReturnTime() {
return this.returnDate === this.outwardDate
? moment(this.outwardDate + ' ' + this.outwardTime).add(this.route.direction.duration, 'seconds').format("HH:mm")
: null;
},
allowedStep: function () {
return m => m % this.timePrecision === 0
}
},
watch: {
initOutwardDate() {
this.outwardDate = this.initOutwardDate;
},
},
created:function(){
// moment.locale(this.locale); // DEFINE DATE LANGUAGE
this.setData();
},
methods: {
change() {
let validSchedules = JSON.parse(JSON.stringify(this.activeSchedules)); // little tweak to deep copy :)
for (var i=0;i<validSchedules.length;i++) {
if (!((validSchedules[i].mon || validSchedules[i].tue || validSchedules[i].wed || validSchedules[i].thu || validSchedules[i].fri || validSchedules[i].sat || validSchedules[i].sun) && (validSchedules[i].outwardTime || validSchedules[i].returnTime))) {
validSchedules.splice(i);
} else {
delete validSchedules[i].id;
delete validSchedules[i].visible;
delete validSchedules[i].menuOutwardTime;
delete validSchedules[i].menuReturnTime;
}
}
this.$emit("change", {
outwardDate: this.outwardDate,
fullschedule : this.schedules,
outwardTime: this.outwardTime,
returnDate: this.returnDate,
returnTime: this.returnTime,
returnTrip: this.returnTrip,
schedules: validSchedules,
returnTimeIsValid: this.returnTimeIsValid,
});
},
changeDate() {
this.clearOtherFields();
this.change();
},
changeTime() {
if (typeof this.$refs.menuOutwardTime !== 'undefined') {
this.$refs.menuOutwardTime.save(this.outwardTime);
}
this.change();
},
checkDateReturn(e){
this.returnTrip = !!e;
if (typeof this.$refs.menuReturnTime != 'undefined') {
this.$refs.menuReturnTime.save(this.returnTime);
}
this.change();
},
clearOtherFields(){
this.outwardTime = null;
this.returnDate = null;
this.returnTime = null;
},
blockTimeRegular(e,id){
let timeSplitted = e.split(':');
this.schedules[id]["minReturnTime"] = moment().hours(timeSplitted[0]).minutes(timeSplitted[1]).add(this.route.direction.duration, 'seconds').format("HH:mm");
// test to allow return time to be set before outward time for regular work
if(id !== 0 && this.schedules[id-1]['returnTime'] === null) {
// console.error("");
} else {
this.schedules[id].maxTimeFromOutwardRegular = e;
}
this.change();
},
checkReturnDesactivate(e){
if (!e) {
this.returnDate = null;
this.returnTime = null;
this.returnTrip = false;
this.change();
}
},
getValueCheckbox(event,item,day){
//If we have more than 1 day and checkbox is set to true
if (this.activeSchedules.length > 1){
var id = item.id;
//We check a day
//Patch : we un disabled the currents time for block selected, see TODO
this.schedules[id].outwardDisabled = false;
this.schedules[id].returnDisabled = false;
if (event) {
this.verifCurrentdDayInAllSchedules(day,id)
// We uncheck a day
}else{
// TODO verif if all schedule have current day open, not just the currrent one
}
}
this.change();
},
verifCurrentdDayInAllSchedules(day,currentSchedule){
//Loop in active shcedules to find others day check
for (var j in this.activeSchedules) {
var c = this.activeSchedules[j];
// Check if not active shcedule , then loop for check if other schedule have same day check
if (c.id !== currentSchedule) {
if (c.mon && day === 'mon') this.checkOutwardReturnAndDisabled(c);
if (c.tue && day === 'tue') this.checkOutwardReturnAndDisabled(c);
if (c.wed && day === 'wed') this.checkOutwardReturnAndDisabled(c);
if (c.thu && day === 'thu') this.checkOutwardReturnAndDisabled(c);
if (c.fri && day === 'fri') this.checkOutwardReturnAndDisabled(c);
if (c.sat && day === 'sat') this.checkOutwardReturnAndDisabled(c);
if (c.sun && day === 'sun') this.checkOutwardReturnAndDisabled(c);
}
}
},
checkOutwardReturnAndDisabled(c){
if (c.outwardTime) {
for (var k in this.activeSchedules) {
var v = this.activeSchedules[k];
if (v.id !== c.id) {
this.activeSchedules[k].outwardDisabled = true;
this.activeSchedules[k].outwardTime = null;
}
}
}
if (c.returnTime) {
for (var l in this.activeSchedules) {
var b = this.activeSchedules[l];
if (b.id !== c.id) {
this.activeSchedules[l].returnDisabled = true;
this.activeSchedules[l].returnTime = null;
}
}
}
},
//Fill array for verification time + date
setData(){
if (!isEmpty(this.initSchedule)) {
let schedule = Array.isArray(this.initSchedule) ? this.initSchedule[0] : this.initSchedule;
let tempSchedules = [];
let days = clone(this.arrayDay);
this.arrayDay.forEach(day => {
if (schedule[day] === true) {
tempSchedules.push({
day: day,
outwardTime: schedule[day + 'OutwardTime'],
returnTime: schedule[day + 'ReturnTime']
});
}
});
let schedulesLength = tempSchedules.length;
for (let i = 0; i < schedulesLength; i++) {
if (!days.includes(tempSchedules[i].day)) continue;
let tempDays = tempSchedules.filter(elem => {return elem.outwardTime === tempSchedules[i].outwardTime && elem.returnTime === tempSchedules[i].returnTime});
this.schedules.push({
id: i,
visible: true,
mon: tempDays.some(day => {return day.day === 'mon'}),
tue: tempDays.some(day => {return day.day === 'tue'}),
wed: tempDays.some(day => {return day.day === 'wed'}),
thu: tempDays.some(day => {return day.day === 'thu'}),
fri: tempDays.some(day => {return day.day === 'fri'}),
sat: tempDays.some(day => {return day.day === 'sat'}),
sun: tempDays.some(day => {return day.day === 'sun'}),
outwardTime: moment(tempSchedules[i].outwardTime).isValid() ? moment(tempSchedules[i].outwardTime).utc().format("HH:mm") : null,
returnTime: moment(tempSchedules[i].returnTime).isValid() ? moment(tempSchedules[i].returnTime).utc().format("HH:mm") : null,
menuOutwardTime: false,
menuReturnTime: false,
outwardDisabled: false,
returnDisabled: false,
maxTimeFromOutwardRegular: null
});
tempDays.forEach(el => {
remove(days, day => {
return el.day === day;
})
})
}
}
this.change();
//Fill array schedules
for (let j = this.schedules.length; j < 7; j++){
this.schedules.push({
id: j,
visible: j === 0,
mon: false,
tue: false,
wed: false,
thu: false,
fri: false,
sat: false,
sun: false,
outwardTime: null,
returnTime: null,
menuOutwardTime: false,
menuReturnTime: false,
outwardDisabled : false,
returnDisabled : false,
maxTimeFromOutwardRegular : null
});
}
this.change();
},
clearOutwardDate() {
this.outwardDate = null;
this.change();
},
clearOutwardTime() {
this.outwardTime = null;
this.change();
},
clearReturnDate() {
this.returnDate = null;
this.change();
},
clearReturnTime() {
this.returnTime = null;
this.change();
},
closeOutwardTime(id) {
for (var i in this.schedules) {
if (this.schedules[i].id == id) {
this.schedules[i].menuOutwardTime = false;
break;
}
}
},
closeReturnTime(id) {
for (var i in this.schedules) {
if (this.schedules[i].id == id) {
this.schedules[i].menuReturnTime = false;
break;
}
}
this.change();
},
removeSchedule(id) {
for (var i in this.schedules) {
if (this.schedules[i].id == id) {
this.schedules[i].visible = false;
break;
}
}
this.change();
},
addSchedule() {
for (var i in this.schedules) {
if (!this.schedules[i].visible) {
this.schedules[i].visible = true;
break;
}
}
this.change();
},
}
};
</script>