755 lines
32 KiB
Vue
755 lines
32 KiB
Vue
<template>
|
||
<!-- TODO: order by name, status -->
|
||
<div ref="macroschedule" class="macroschedule">
|
||
<div class="row mb-3">
|
||
<div class="col-1">
|
||
<button class="btn btn-primary" @click.prevent="goPreviousWeek()">←</button>
|
||
</div>
|
||
<div class="col">
|
||
<h1 class="text-secondary">Plan de charge</h1>
|
||
<div class="input-group">
|
||
<label for="scheduleDateSelect" class="input-group-text">Voir date : </label>
|
||
<input id="scheduleDateSelect" class="form-control" type="date" :value="startMoment.format('YYYY-MM-DD')" @input="showMacroScheduleDate($event.target.value)">
|
||
</div>
|
||
</div>
|
||
<div class="col">
|
||
<fieldset>
|
||
<legend>Légende</legend>
|
||
<template v-if="isAdmin">
|
||
<span v-for="activityEnum of activitiesEnum" :key="activityEnum.value" :class="'p-1 m-2 border badge badge-activity-' + activityEnum.value">{{ activityEnum.label }}</span>
|
||
<span class="p-1 m-2 border badge badge-marker">Marqueur</span>
|
||
<span class="p-1 m-2 border badge badge-today">Aujourd'hui</span>
|
||
</template>
|
||
<template v-else>
|
||
<span class="p-1 m-2 border badge badge-activity-light">Libre</span>
|
||
<span class="p-1 m-2 border badge badge-activity-dark">Occupé</span>
|
||
</template>
|
||
</fieldset>
|
||
</div>
|
||
<div class="col-1">
|
||
<button class="btn btn-primary" @click.prevent="goNextWeek()">→</button>
|
||
</div>
|
||
</div>
|
||
<div class="row justify-content-center">
|
||
<div class="col-12">
|
||
<table class="table table-bordered">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col" rowspan="3" class="align-middle">{{ isAdmin ? 'Projets' : '' }}</th>
|
||
<th scope="col" rowspan="3" class="align-middle">{{ isAdmin ? 'Statut' : '' }}</th>
|
||
<th v-if="daysThisMonth.length" :colspan="daysThisMonth.length" class="text-center">
|
||
{{ daysThisMonth[0].format('MMMM') | ucfirst }}
|
||
</th>
|
||
<th v-if="daysNextMonth.length" :colspan="daysNextMonth.length" class="text-center">
|
||
{{ daysNextMonth[0].format('MMMM') | ucfirst }}
|
||
</th>
|
||
</tr>
|
||
<tr>
|
||
<th v-for="weekNumber in macroscheduleConfig.numberOfWeeksToShow" :key="weekNumber" :colspan="7 - 2" class="text-center">
|
||
SEMAINE {{ currentWeek + weekNumber - 1}}
|
||
</th>
|
||
</tr>
|
||
<tr>
|
||
<template v-for="(currentDay, index) of daysToShow">
|
||
<th scope="col" v-if="dayShouldbeenSeen(currentDay)" :key="index" :class="'dayCell p-0 text-center ' + (currentDay.isSame(todayMoment, 'day') ? 'today' : '')">
|
||
{{ currentDay.format('dd') }} {{ String(currentDay.date()).padStart(2, '0') }}
|
||
</th>
|
||
</template>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<template v-if="isAdmin">
|
||
<tr v-for="project of projects" :key="project.id" class="projectRow">
|
||
<th scope="col">
|
||
<span :class="'rounded-1 p-1 project text-nowrap text-truncate overflow-hidden bg-enum-'+project.status">{{ project.name | str_limit(45) }}</span>
|
||
</th>
|
||
<td>
|
||
<span class="fw-bold text-nowrap text-truncate overflow-hidden">{{ projectStatusEnum[project.status].label }}</span>
|
||
</td>
|
||
<template v-for="(currentDay, index) of daysToShow">
|
||
<td :class="'p-0 border border-dark activities ' + (currentDay.isSame(todayMoment, 'day') ? 'today' : '')" v-if="dayShouldbeenSeen(currentDay)" :key="index">
|
||
<div
|
||
v-for="(activity, index) in [getActivity(project, getMorningMoment(currentDay)), getActivity(project, getAfterNoonMoment(currentDay))]"
|
||
:key="index"
|
||
:class="getActivityClass(activity)+ ' activity ' + (hoverActivityId === activity.id ? 'hover' : '')"
|
||
@click="showModale(project, activity, currentDay, index === 0)"
|
||
@mouseover="setActivityHover(activity)"
|
||
@mouseleave="setActivityHover(activity, false)">
|
||
<div :title="getActivityTooltip(activity)" data-bs-toggle="tooltip">
|
||
|
||
</div>
|
||
<template v-if="projectHasMarkersForTheDay(currentDay, project)">
|
||
<div
|
||
class="marker"
|
||
data-bs-toggle="tooltip"
|
||
data-bs-html="true"
|
||
:title="getMarkersTooltip(currentDay, project)"></div>
|
||
</template>
|
||
</div>
|
||
</td>
|
||
</template>
|
||
</tr>
|
||
</template>
|
||
<tr class="projectRow">
|
||
<th scope="col" class="border-end-0 d-flex justify-content-between"><span class="fw-bold h5">Disponibilité</span></th>
|
||
<td class="border-start-0"></td>
|
||
<template v-for="(currentDay, index) of daysToShow">
|
||
<td :class="'p-0 border border-dark activities ' + (currentDay.isSame(todayMoment, 'day') ? 'today' : '')" v-if="dayShouldbeenSeen(currentDay)" :key="index">
|
||
<template v-if="isAdmin">
|
||
<div class="activity text-center" :style="{ 'background-color' : 'rgba(0,0,0,'+ getLoadPercent(getMorningMoment(currentDay)) }">
|
||
{{ getLoad(getMorningMoment(currentDay)) > 1 ? '🔥' : ' ' }}
|
||
</div>
|
||
<div class="activity text-center" :style="{ 'background-color' : 'rgba(0,0,0,'+ getLoadPercent(getAfterNoonMoment(currentDay)) }">
|
||
{{ getLoad(getAfterNoonMoment(currentDay)) > 1 ? '🔥' : ' ' }}
|
||
</div>
|
||
</template>
|
||
<template v-else>
|
||
<div :class="'activity text-center ' +(getLoad(getMorningMoment(currentDay)) > macroscheduleConfig.loadDevThreshold ? 'bg-dark' : 'bg-light')"> </div>
|
||
<div :class="'activity text-center ' +(getLoad(getAfterNoonMoment(currentDay)) > macroscheduleConfig.loadDevThreshold ? 'bg-dark' : 'bg-light')"> </div>
|
||
</template>
|
||
</td>
|
||
</template>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<!--
|
||
- une Date début, et un choix matin/après-midi
|
||
- une Date Fin, et un choix matin/après-midi
|
||
- une activité (cf. liste annexe ci-dessous)
|
||
- une charge (en nombre d’hommes occupés)
|
||
- un commentaire
|
||
-->
|
||
<div ref="editModal" class="modal fade" tabindex="-1" aria-labelledby="modalLabel" aria-hidden="true" >
|
||
<div class="modal-dialog modal-lg">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="modalLabel" ref="modalLabel">Activité pour {{ modaleProps.name }}</h5>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="container">
|
||
<div class="row justify-content-center">
|
||
<div v-if="error" class="alert alert-danger" role="alert">
|
||
{{ error }}
|
||
</div>
|
||
<div class="col-8">
|
||
<div class="form-group mb-3">
|
||
<label for="categorie" class="col-form-label text-md-right ms-5">Activité</label>
|
||
<select id="categorie" ref="type" class="form-select" aria-label="Sélectionnez une catégorie" :disabled="modalLoading" v-model="modaleProps.activity" required>
|
||
<option :value="undefined" disabled>Sélectionnez une activité</option>
|
||
<option v-for="activityEnum in activitiesEnum" :key="activityEnum.value" :value="activityEnum.value" :class="activityEnum.value">{{ activityEnum.label }}</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group mb-3">
|
||
<label for="start" class="col-form-label text-md-right ms-5">Date début</label>
|
||
<div class="input-group mb-3">
|
||
<input class="form-control" id="start" type="date" name="start" :value="moment(modaleProps.start).format('YYYY-MM-DD')" @input="updateDateField($event, 'start')" :disabled="modalLoading">
|
||
<select class="form-select" :disabled="modalLoading" v-model="modaleProps.startMorning" @input="updateHourField($event, 'start')">
|
||
<option value="1" :selected="moment(modaleProps.start).format('HH') === '00'">Matin</option>
|
||
<option value="0" :selected="moment(modaleProps.start).format('HH') === '12'">Midi</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-group mb-3">
|
||
<label for="end" class="col-form-label text-md-right ms-5">Date fin</label>
|
||
<div class="input-group mb-3">
|
||
<input class="form-control" id="end" type="date" name="end" :value="moment(modaleProps.end).format('YYYY-MM-DD')" @input="updateDateField($event, 'end')" :disabled="modalLoading">
|
||
<select class="form-select" :disabled="modalLoading" v-model="modaleProps.endMorning" @input="updateHourField($event, 'end')">
|
||
<option value="1" :selected="moment(modaleProps.end).format('HH') === '11'">Midi</option>
|
||
<option value="0" :selected="moment(modaleProps.end).format('HH') === '23'">Soir</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-group mb-3">
|
||
<label for="comment" class="col-form-label text-md-right ms-5">Commentaire</label>
|
||
<textarea ref="comment" class="form-control" name="comment" id="comment" rows="2" placeholder="" @keydown="limitLines($event)" v-model="modaleProps.comment" :disabled="modalLoading"></textarea>
|
||
</div>
|
||
<div class="form-group mb-3">
|
||
<label for="load" class="col-form-label text-md-right ms-5">Charge <i>(nombre de personnes consommées sur la période)</i></label>
|
||
<input type="number" class="form-control" name="load" id="load" v-model="modaleProps.load" :disabled="modalLoading" min="1"/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row justify-content-center">
|
||
<div class="col-8 text-center">
|
||
<button class="btn btn-primary me-3" type="button" :disabled="modalLoading" @click="saveActivity()">
|
||
<span v-if="modalLoading" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||
{{ modalLoading ? 'Chargement...' : 'Valider' }}
|
||
</button>
|
||
<button class="btn btn-danger" @click="modale.hide();$nextTick(refreshTooltips)" :disabled="modalLoading">Fermer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import Toastify from 'toastify-js'
|
||
import axios from 'axios';
|
||
|
||
export default {
|
||
props: {
|
||
isAdmin: {
|
||
type: Number,
|
||
},
|
||
dbLoadCollection: {
|
||
type: String,
|
||
},
|
||
dbMacroscheduleConfig: {
|
||
type: String,
|
||
},
|
||
dbProjects: {
|
||
type: String,
|
||
},
|
||
dbActivitiesEnum: {
|
||
type: String,
|
||
},
|
||
dbProjectStatusEnum: {
|
||
type: String,
|
||
},
|
||
today: {
|
||
type: String,
|
||
},
|
||
startDay: {
|
||
type: String,
|
||
}
|
||
},
|
||
mounted() {
|
||
let self = this
|
||
window.Toastify = Toastify
|
||
this.projects = JSON.parse(this.dbProjects)
|
||
this.loadCollection = JSON.parse(this.dbLoadCollection)
|
||
this.macroscheduleConfig = JSON.parse(this.dbMacroscheduleConfig)
|
||
this.activitiesEnum = JSON.parse(this.dbActivitiesEnum)
|
||
this.projectStatusEnum = JSON.parse(this.dbProjectStatusEnum)
|
||
this.$nextTick(this.refreshTooltips)
|
||
this.modale = new bootstrap.Modal(this.$refs.editModal)
|
||
this.$refs.editModal.addEventListener('hide.bs.modal', function (e) {
|
||
if(self.modalLoading) {
|
||
e.preventDefault()
|
||
return false
|
||
}
|
||
})
|
||
},
|
||
data() {
|
||
return {
|
||
textLimit: 254,
|
||
hoverActivityId: null,
|
||
error: '',
|
||
modaleProps: {
|
||
activity: null,
|
||
start: '',
|
||
startMorning: 1,
|
||
end: '',
|
||
endMorning: 1,
|
||
load: 0,
|
||
comment: '',
|
||
name: '',
|
||
project: null
|
||
},
|
||
currentActivity: null,
|
||
projects: [],
|
||
activitiesEnum: [],
|
||
projectStatusEnum: [],
|
||
macroscheduleConfig: [],
|
||
loadCollection: {},
|
||
modale: null,
|
||
modalLoading: false,
|
||
tooltipTriggerList: []
|
||
};
|
||
},
|
||
computed: {
|
||
todayMoment() {
|
||
return this.moment(this.today)
|
||
},
|
||
startMoment() {
|
||
return this.moment(this.startDay)
|
||
},
|
||
selectedMoment() {
|
||
return this.moment(this.startMoment.week(), 'w').hours(0).minutes(0).seconds(0).milliseconds(0).add(1, 'day').startOf('week')
|
||
},
|
||
currentMonthName() {
|
||
return this.selectedMoment.format('MMMM')
|
||
},
|
||
currentWeek() {
|
||
return this.selectedMoment.week()
|
||
},
|
||
daysToShow() {
|
||
let days = [], startMoment = this.selectedMoment.clone()
|
||
for (let i = this.macroscheduleConfig.numberOfWeeksToShow; i > 0; i--) {
|
||
for(let dayNumber = 0; dayNumber < 7; dayNumber++) {
|
||
days.push(startMoment)
|
||
startMoment = startMoment.clone().add(1, 'day')
|
||
}
|
||
}
|
||
return days
|
||
},
|
||
daysThisMonth() {
|
||
let days = this.daysToShow, currentMonth = days[0]?.month(),
|
||
out = []
|
||
for (let day of days) {
|
||
if(currentMonth === day.month() && this.dayShouldbeenSeen(day)) {
|
||
out.push(day)
|
||
}
|
||
}
|
||
return out
|
||
},
|
||
daysNextMonth() {
|
||
let days = this.daysToShow, currentMonth = days[0]?.month(),
|
||
out = []
|
||
for (let day of days) {
|
||
if(currentMonth !== day.month() && this.dayShouldbeenSeen(day)) {
|
||
out.push(day)
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
},
|
||
watch: {
|
||
modaleProps: {
|
||
handler(object) {
|
||
// * Validate Date fields
|
||
this.error = ''
|
||
this.validateDateField(this.moment(this.modaleProps.start), 'start')
|
||
this.validateDateField(this.moment(this.modaleProps.end), 'end')
|
||
},
|
||
deep: true
|
||
},
|
||
currentActivity: {
|
||
handler(activityObject) {
|
||
if (activityObject.project_id) {
|
||
let project = this.getProject(activityObject.project_id)
|
||
// * Insert (or update) activity
|
||
this.$set(project.activities, activityObject.id, Object.assign({}, activityObject))
|
||
this.$nextTick(this.refreshTooltips)
|
||
this.$forceUpdate()
|
||
}
|
||
},
|
||
deep: true
|
||
}
|
||
},
|
||
methods: {
|
||
validateDateField(moment, fieldName) {
|
||
if(!this.dayShouldbeenSeen(moment)) {
|
||
this.error = 'La date de ' + (fieldName === 'start' ? 'début' : 'fin') + ' ne peut être un jour de weekend.'
|
||
return false
|
||
}
|
||
return true
|
||
},
|
||
updateDateField(e, fieldName) {
|
||
let date = this.moment(this.modaleProps[fieldName]),
|
||
time = Number.parseInt(this.modaleProps[fieldName + 'Morning']) === 1 ?
|
||
this.getMorningMoment(date, fieldName === 'start') :
|
||
this.getAfterNoonMoment(date, fieldName === 'start'),
|
||
input = this.moment(e.target.value)
|
||
if(this.validateDateField(input, fieldName)) {
|
||
this.error = ''
|
||
this.$set(this.modaleProps, fieldName, input.format('YYYY-MM-DD') + 'T' + time.format('HH:mm:ss'))
|
||
}
|
||
},
|
||
updateHourField(e, fieldName) {
|
||
let date = this.moment(this.modaleProps[fieldName])
|
||
if(Number.parseInt(e.target.value) === 1) {
|
||
this.$set(this.modaleProps, fieldName, this.getMorningMoment(date, fieldName === 'start').format('YYYY-MM-DD\THH:mm:ss'))
|
||
} else {
|
||
this.$set(this.modaleProps, fieldName, this.getAfterNoonMoment(date, fieldName === 'start').format('YYYY-MM-DD\THH:mm:ss'))
|
||
}
|
||
},
|
||
saveActivity() {
|
||
let self = this,
|
||
activity = Object.assign({}, this.currentActivity)
|
||
|
||
if(this.error.length) {
|
||
return;
|
||
}
|
||
|
||
self.modalLoading = true
|
||
|
||
activity.activity = self.modaleProps.activity
|
||
activity.start = self.modaleProps.start
|
||
activity.end = self.modaleProps.end
|
||
activity.load = self.modaleProps.load
|
||
activity.comment = self.modaleProps.comment
|
||
activity.project_id = self.modaleProps.project.id
|
||
|
||
axios.post(self.route('macroplanning.activity'), activity)
|
||
.then(response => {
|
||
self.error = ''
|
||
self.sendToast('L\'activité à été mis à jour')
|
||
self.$nextTick(() => {
|
||
self.modalLoading = false
|
||
self.modale.hide()
|
||
activity.id = response.data.id
|
||
self.$set(self, 'currentActivity', activity)
|
||
})
|
||
if(response.data.hasOverlap) {
|
||
window.location.reload()
|
||
}
|
||
}).catch(error => {
|
||
if(error.response.status === 429) {
|
||
location.reload()
|
||
}
|
||
if(error.response.status === 422) {
|
||
if(error.response.data.errors) {
|
||
self.error = ''
|
||
for(let id in error.response.data.errors) {
|
||
let field = error.response.data.errors[id]
|
||
self.error += field.join('\n') + '\n'
|
||
}
|
||
return
|
||
} else if(error.response.data.message) {
|
||
self.error = error.response.data.message
|
||
return
|
||
}
|
||
}
|
||
self.error = error
|
||
}).then(() => {
|
||
self.modalLoading = false
|
||
self.$nextTick(self.refreshTooltips)
|
||
})
|
||
},
|
||
showModale(project, activity, selectedMoment, isMorning = true) {
|
||
|
||
selectedMoment = selectedMoment.clone()
|
||
|
||
let self = this,
|
||
startMoment = (activity.start ? this.moment(activity.start) :
|
||
(isMorning ? this.getMorningMoment(selectedMoment, true) : this.getAfterNoonMoment(selectedMoment, true))),
|
||
endMoment = (activity.end ? this.moment(activity.end) :
|
||
(isMorning ? this.getMorningMoment(selectedMoment, false) : this.getAfterNoonMoment(selectedMoment, false)))
|
||
|
||
this.modaleProps = {
|
||
activity: activity.activity,
|
||
start: activity.start ?? startMoment.format('YYYY-MM-DD\THH:mm:ss'),
|
||
startMorning: startMoment.format('HH') === '00' ? 1 : 0,
|
||
end: activity.end ?? endMoment.format('YYYY-MM-DD\THH:mm:ss'),
|
||
endMorning: endMoment.format('HH') === '11' ? 1 : 0,
|
||
load: activity.load,
|
||
comment: activity.comment,
|
||
name: project.name,
|
||
project: project
|
||
}
|
||
this.currentActivity = activity
|
||
this.modale.show()
|
||
|
||
setTimeout(() => {
|
||
self.$nextTick(self.refreshTooltips)
|
||
}, 2000)
|
||
},
|
||
dayShouldbeenSeen(moment) {
|
||
return moment.day() !== 6 && moment.day() !== 0
|
||
},
|
||
projectHasMarkersForTheDay(moment, project) {
|
||
for(let marker of project.markers) {
|
||
if (moment.isSame(marker.marker, 'day')) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
},
|
||
|
||
setActivityHover(activity, activate = true) {
|
||
if(!activate) {
|
||
this.hoverActivityId = null
|
||
} else {
|
||
this.hoverActivityId = activity.activity !== undefined ? activity.id : ''
|
||
}
|
||
},
|
||
|
||
// * GETTERS
|
||
getLoadPercent(moment) {
|
||
let load = this.getLoad(moment)
|
||
return load > 1 ? 1 : load
|
||
},
|
||
getLoad(moment) {
|
||
return this.loadCollection[moment.format('YYYY-MM-DD 00:00:00')] ?? null
|
||
},
|
||
getActivityTooltip(activity) {
|
||
let activityEnumLabel = activity.activity !== undefined ? (this.activitiesEnum[activity.activity] ? this.activitiesEnum[activity.activity].label : '') : ''
|
||
return activity.activity !== undefined ? `${activityEnumLabel}, Charge ${activity.load} personnes, ${activity.comment}` : ''
|
||
},
|
||
getActivityClass(activity) {
|
||
return activity ? `bg-enum-${activity.activity}` : 'bg-white'
|
||
},
|
||
getProject(project_id) {
|
||
return this.projects[project_id]
|
||
},
|
||
getActivity(project, moment) {
|
||
for(let activity of Object.values(project.activities)) {
|
||
if(moment.isBetween(this.moment(activity.start, this.moment.DATETIME_LOCAL_SECONDS), this.moment(activity.end, this.moment.DATETIME_LOCAL_SECONDS), 'hour', '[]')) {
|
||
return activity
|
||
}
|
||
}
|
||
return {}
|
||
},
|
||
getMorningMoment(moment, start = true) {
|
||
moment = moment.clone()
|
||
return start ? moment.hours(0).minutes(0).seconds(0) : moment.hours(11).minutes(59).seconds(0)
|
||
},
|
||
getAfterNoonMoment(moment, start = true) {
|
||
moment = moment.clone()
|
||
return start ? moment.hours(12).minutes(0).seconds(0) : moment.hours(23).minutes(59).seconds(0)
|
||
},
|
||
getMarkersTooltip(moment, project) {
|
||
let tooltip = ''
|
||
for(let marker of project.markers) {
|
||
if (moment.isSame(marker.marker, 'day')) {
|
||
tooltip += `${this.moment(marker.marker).format('DD/MM/YYYY')} ${marker.label}<br>`
|
||
}
|
||
}
|
||
return tooltip
|
||
},
|
||
|
||
// * NAVIGATION
|
||
goPreviousWeek() {
|
||
let moment = this.startMoment ?? this.todayMoment
|
||
moment = this.moment(moment.week(), 'w')
|
||
this.showMacroScheduleDate('', moment.subtract(1, 'week'))
|
||
},
|
||
goNextWeek() {
|
||
let moment = this.startMoment ?? this.todayMoment
|
||
moment = this.moment(moment.week(), 'w')
|
||
this.showMacroScheduleDate('', moment.add(1, 'week'))
|
||
},
|
||
showMacroScheduleDate(dateString, moment = null) {
|
||
let url = new URL(window.location)
|
||
moment = moment ?? this.moment(dateString)
|
||
location = url.origin+ url.pathname + '?date='+moment.format('DD/MM/YYYY')
|
||
},
|
||
|
||
sendToast(message) {
|
||
Toastify({
|
||
text: message,
|
||
duration: 3000,
|
||
gravity: "top",
|
||
position: "right",
|
||
offset: {
|
||
x: '2rem',
|
||
y: '3rem'
|
||
},
|
||
style: {
|
||
background: "linear-gradient(to right, #00317d, #0058ca)",
|
||
}
|
||
}).showToast();
|
||
},
|
||
refreshTooltips() {
|
||
let exisitingToolips = document.querySelectorAll('.tooltip[role="tooltip"]')
|
||
for(let tooltip of exisitingToolips) {
|
||
tooltip.remove()
|
||
}
|
||
this.tooltipTriggerList = [].slice.call(this.$refs.macroschedule.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||
this.tooltipTriggerList.map((tooltipTriggerEl) => {
|
||
return new bootstrap.Tooltip(tooltipTriggerEl, { delay: 500 })
|
||
})
|
||
},
|
||
limitLines(e) {
|
||
let keynum, lines = this.$refs.comment.value.split('\n').length;
|
||
// IE
|
||
if(window.event) {
|
||
keynum = e.keyCode;
|
||
// Netscape/Firefox/Opera
|
||
} else if(e.which) {
|
||
keynum = e.which;
|
||
}
|
||
if(keynum != 8 && this.$refs.comment.value.length >= this.textLimit) {
|
||
e.preventDefault()
|
||
return false;
|
||
}
|
||
if(keynum == 13 && lines == this.$refs.comment.rows) {
|
||
e.preventDefault()
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import './../../sass/variables';
|
||
@import './../../sass/complement';
|
||
@import "toastify-js/src/toastify.css";
|
||
|
||
.macroschedule {
|
||
@each $index, $color in $enum-activities {
|
||
.badge-activity-#{$index} {
|
||
background-color: $color;
|
||
color: a11y-color(#353535 ,$color);
|
||
}
|
||
}
|
||
.badge-marker {
|
||
background-color: rgba(255, 2, 204, 0.6);
|
||
color: a11y-color(#353535 ,rgba(255, 2, 204, 0.6));
|
||
}
|
||
.badge-today {
|
||
background-color: rgba(0, 255, 0, 0.6);
|
||
color: a11y-color(#353535 ,rgba(0, 255, 0, 0.6));
|
||
}
|
||
.badge-activity-light {
|
||
background-color: $light;
|
||
color: a11y-color(#353535 ,$light);
|
||
}
|
||
.badge-activity-dark {
|
||
background-color: $dark;
|
||
color: a11y-color(#FFF ,$dark);
|
||
}
|
||
|
||
.project {
|
||
@each $index, $color in $enum-project-status {
|
||
&.bg-enum-#{$index} {
|
||
background-color: $color;
|
||
color: a11y-color(#f6f6f6 ,$color);
|
||
}
|
||
}
|
||
}
|
||
|
||
th.today,td.today > div {
|
||
position: relative;
|
||
&:before {
|
||
content: ' ';
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 0.2rem;
|
||
transform: translateY(-50%);
|
||
height: 110%;
|
||
width: 0.3rem;
|
||
background-color: rgba(0, 255, 0, 0.6);
|
||
|
||
}
|
||
}
|
||
tr:last-child > td.today {
|
||
position: relative;
|
||
&:after {
|
||
content: '↑ Aujourd\'hui';
|
||
position: absolute;
|
||
bottom: -2.5rem;
|
||
left: 0;
|
||
background-color: rgb(0, 255, 0);
|
||
padding: 0.2rem;
|
||
border: 1px solid rgba(0, 0, 0, 0.4);
|
||
border-radius: 0.2rem;
|
||
width: 7rem;
|
||
}
|
||
}
|
||
|
||
.dayCell {
|
||
width: 3rem;
|
||
}
|
||
|
||
.projectRow {
|
||
&:not(:first-child) {
|
||
border-top: 2px solid black;
|
||
}
|
||
}
|
||
|
||
.activities .activity {
|
||
&:not(:first-child) {
|
||
border-top: 1px solid black;
|
||
}
|
||
}
|
||
|
||
$buttonBgColor: #4d4d4d;
|
||
|
||
.activity {
|
||
position: relative;
|
||
|
||
.bg-white {
|
||
height: 100%;
|
||
width: 100%;
|
||
}
|
||
|
||
@each $index, $color in $enum-activities {
|
||
&.bg-enum-#{$index} {
|
||
background-color: $color;
|
||
color: a11y-color(#353535 ,$color);
|
||
height: 100%;
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
// Reset button Style to nice one
|
||
button {
|
||
display: inline;
|
||
border: none;
|
||
padding: 0.6rem 3rem;
|
||
margin: 0;
|
||
text-decoration: none;
|
||
background: $buttonBgColor;
|
||
font-size: 1rem;
|
||
cursor: pointer;
|
||
text-align: center;
|
||
transition: background 250ms ease-in-out, transform 150ms ease;
|
||
-webkit-appearance: none;
|
||
-moz-appearance: none;
|
||
}
|
||
|
||
button:disabled,
|
||
button[disabled] {
|
||
color: #aaa;
|
||
}
|
||
|
||
button:hover,
|
||
button:focus {
|
||
background: darken($buttonBgColor, 10%);
|
||
}
|
||
|
||
button:focus {
|
||
outline: 1px solid #fff;
|
||
outline-offset: -4px;
|
||
}
|
||
|
||
button:active {
|
||
transform: scale(0.99);
|
||
}
|
||
|
||
.btn {
|
||
display: none;
|
||
position: absolute;
|
||
top: 0.1rem;
|
||
left: 0.1rem;
|
||
padding: 0;
|
||
width: 1.8rem;
|
||
height: 1.8rem;
|
||
margin: 0;
|
||
background-color: $buttonBgColor;
|
||
|
||
&:hover,
|
||
&:focus {
|
||
background-color: darken($buttonBgColor, 10%);
|
||
}
|
||
&.btn-clear {
|
||
color: darken($color: #fff, $amount: 5);
|
||
}
|
||
}
|
||
|
||
.infos {
|
||
display: none;
|
||
}
|
||
|
||
&:hover {
|
||
cursor: pointer;
|
||
}
|
||
|
||
&.hover > div {
|
||
position: relative;
|
||
z-index: 2;
|
||
background-color: rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
& > div.marker {
|
||
z-index: 200;
|
||
position: absolute;
|
||
top: 50%;
|
||
right: 0.2rem;
|
||
transform: translateY(-50%);
|
||
height: 110%;
|
||
width: 0.7rem;
|
||
background-color: rgba(255, 2, 204, 0.6);
|
||
}
|
||
}
|
||
}
|
||
</style>
|