ldc-supportweb/resources/ts/components/MacroPlanningComponent.vue
2022-08-30 13:21:07 +00:00

1252 lines
38 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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&nbsp;:&nbsp;
</label>
<input
id="scheduleDateSelect"
class="form-control"
type="date"
:value="startMoment.format('YYYY-MM-DD')"
@input="
showMacroScheduleDate(
$event.target ? ($event.target as HTMLInputElement).value : ''
)
"
>
</div>
</div>
<div class="col">
<fieldset>
<legend>Légende</legend>
<template v-if="isAdmin">
<span
v-for="(activityEnum, index) of activitiesEnum"
:key="index"
:class="`p-1 m-2 border badge badge-activity-${String(
activityEnum.value
)}`"
>
{{ String(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"
>
{{ ucfirst(daysThisMonth[0].format("MMMM")) }}
</th>
<th
v-if="daysNextMonth.length"
:colspan="daysNextMonth.length"
class="text-center"
>
{{ ucfirst(daysNextMonth[0].format("MMMM")) }}
</th>
</tr>
<tr v-if="macroscheduleConfig !== null">
<th
v-for="weekNumber in macroscheduleConfig.numberOfWeeksToShow"
:key="weekNumber"
:colspan="7 - 2"
class="text-center"
>
SEMAINE&nbsp;{{ 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") }}&nbsp;{{
String(currentDay.date()).padStart(2, "0")
}}
</th>
</template>
</tr>
</thead>
<tbody>
<template v-if="isAdmin">
<tr
v-for="(project, index) of projects"
:key="index"
class="projectRow"
>
<th scope="col">
<a
:href="getProjectRoute(project)"
:class="
'rounded-1 p-1 project text-nowrap text-truncate overflow-hidden bg-enum-' +
project.status
"
>
{{ str_limit(String(project.name), 45) }}
</a>
</th>
<td>
<span
class="fw-bold text-nowrap text-truncate overflow-hidden"
>
{{
String(projectStatusEnum[project.status as number].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(getMorningMoment(currentDay), project),
getActivity(getAfterNoonMoment(currentDay), project),
]"
: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"
>
&nbsp;
</div>
<template
v-if="projectHasMarkersForTheDay(currentDay, project)"
>
<div
class="marker"
data-bs-toggle="tooltip"
data-bs-html="true"
:title="getMarkersTooltip(currentDay, project)"
/>
</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" />
<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
? "&#128293;"
: "&nbsp;"
}}
</div>
<div
class="activity text-center"
:style="{
'background-color':
'rgba(0,0,0,' +
getLoadPercent(getAfterNoonMoment(currentDay)),
}"
>
{{
getLoad(getAfterNoonMoment(currentDay)) > 1
? "&#128293;"
: "&nbsp;"
}}
</div>
</template>
<template v-else>
<div
v-if="macroscheduleConfig !== null"
:class="
'activity text-center ' +
(getLoad(getMorningMoment(currentDay)) >
macroscheduleConfig.loadDevThreshold
? 'bg-dark'
: 'bg-light')
"
>
&nbsp;
</div>
<div
v-if="macroscheduleConfig !== null"
:class="
'activity text-center ' +
(getLoad(getAfterNoonMoment(currentDay)) >
macroscheduleConfig.loadDevThreshold
? 'bg-dark'
: 'bg-light')
"
>
&nbsp;
</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, index) in activitiesEnum"
:key="index"
: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="
momentJs(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="
momentJs(modaleProps.start).format('HH') === '00'
"
>
Matin
</option>
<option
value="0"
:selected="
momentJs(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="momentJs(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="
momentJs(modaleProps.end).format('HH') === '11'
"
>
Midi
</option>
<option
value="0"
:selected="
momentJs(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"
/>
</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"
/>
{{ modalLoading ? "Chargement..." : "Valider" }}
</button>
<button
class="btn btn-danger"
@click.prevent="
modale ? modale.hide() : null;
$nextTick(refreshTooltips);
"
:disabled="modalLoading"
>
Fermer
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import * as Toastify from "toastify-js";
import axios from "axios";
import trans from "../modules/trans";
import route from "../modules/route";
import { Moment } from "moment";
import * as momentJs from "moment";
import type { Modal } from "bootstrap";
export default defineComponent({
mixins: [trans, route, { methods: { momentJs } }],
mounted() {
let self = this;
window.Toastify = Toastify;
const json = String(this.$attrs.json ?? "{}"),
data = JSON.parse(json);
this.projects = data.projects;
this.loadCollection = data.loadCollection;
this.macroscheduleConfig = data.macroscheduleConfig;
this.activitiesEnum = data.activitiesEnum;
this.projectStatusEnum = data.projectStatusEnum;
this.today = data.today;
this.startDay = data.startDay;
this.isAdmin = data.isAdmin;
this.$nextTick(this.refreshTooltips);
this.modale = new window.bootstrap.Modal(
this.$refs.editModal as HTMLElement
);
(this.$refs.editModal as HTMLElement).addEventListener(
"hide.bs.modal",
function (e: Event) {
if (self.modalLoading) {
e.preventDefault();
return false;
}
}
);
this.initTooltips();
},
data(): {
isAdmin: boolean;
today: string;
startDay: string;
textLimit: number;
hoverActivityId: string | null;
error: string | undefined;
modaleProps: {
[key: string]: LaravelModel | number | string | null | 1 | 0;
activity: number | null;
start: string;
startMorning: 1 | 0;
end: string;
endMorning: 1 | 0;
load: number;
comment: string;
name: string;
project: LaravelModel | null;
};
currentActivity: LaravelModel | null;
projects: LaravelModelList;
activitiesEnum: LaravelModelList;
projectStatusEnum: LaravelModelList;
macroscheduleConfig: {
loadDevThreshold: number;
numberOfWeeksToShow: number;
} | null;
loadCollection: Record<string, number> | null;
modale: Modal | null;
modalLoading: boolean;
} {
return {
isAdmin: false,
today: "",
startDay: "",
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: null,
loadCollection: {},
modale: null,
modalLoading: false,
};
},
computed: {
todayMoment(): Moment {
return this.momentJs(this.today);
},
startMoment(): Moment {
return this.momentJs(this.startDay);
},
selectedMoment(): Moment {
return this.momentJs(this.startMoment.week(), "w")
.hours(0)
.minutes(0)
.seconds(0)
.milliseconds(0)
.add(1, "day")
.startOf("week");
},
currentMonthName(): string {
return this.selectedMoment.format("MMMM");
},
currentWeek(): number {
return this.selectedMoment.week();
},
daysToShow(): Moment[] {
let days = [],
startMoment = this.selectedMoment.clone();
if (!this.macroscheduleConfig) {
return [];
}
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(): Moment[] {
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(): Moment[] {
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() {
// * Validate Date fields
this.error = "";
this.validateDateField(this.momentJs(this.modaleProps.start), "start");
this.validateDateField(this.momentJs(this.modaleProps.end), "end");
},
deep: true,
},
currentActivity: {
handler(activityObject: LaravelModel | null) {
if (activityObject && activityObject.project_id) {
const project = this.getProject(activityObject.project_id as number);
// * Insert (or update) activity
(project.activities as LaravelModelList)[
activityObject.id as number
] = Object.assign({}, activityObject);
this.$nextTick(this.refreshTooltips);
this.$forceUpdate();
}
},
deep: true,
},
},
methods: {
validateDateField(moment: Moment, fieldName: string): boolean {
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: Event, fieldName: string): void {
const date = this.momentJs(this.modaleProps[fieldName]),
target = e.target,
time =
Number.parseInt(String(this.modaleProps[`${fieldName}Morning`])) === 1
? this.getMorningMoment(date, fieldName === "start")
: this.getAfterNoonMoment(date, fieldName === "start"),
input = target
? this.momentJs((target as HTMLInputElement).value)
: null;
if (!input) {
throw new Error("updateDateField target input is undefined");
}
if (this.validateDateField(input, fieldName)) {
this.error = "";
this.modaleProps[fieldName] =
input.format("YYYY-MM-DD") + "T" + time.format("HH:mm:ss");
}
},
updateHourField(e: Event, fieldName: string): void {
const date = this.momentJs(this.modaleProps[fieldName]),
target = e.target;
if (target && Number.parseInt((target as HTMLInputElement).value) === 1) {
this.modaleProps[fieldName] = this.getMorningMoment(
date,
fieldName === "start"
).format("YYYY-MM-DDTHH:mm:ss");
} else {
this.modaleProps[fieldName] = this.getAfterNoonMoment(
date,
fieldName === "start"
).format("YYYY-MM-DDTHH:mm:ss");
}
},
saveActivity(): void {
const self = this,
activity = Object.assign({}, this.currentActivity) as LaravelModel,
routeUri = self.route("macroplanning.activity");
if (self.error?.length) {
return;
}
if (!routeUri) {
throw new Error("macroplanning.activity is undefined");
}
self.modalLoading = true;
if (self.modaleProps.activity === null) {
throw new Error("modaleProps.activity is null");
}
if (self.modaleProps.project === null) {
throw new Error("modaleProps.project is null");
}
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(routeUri, activity)
.then((response) => {
self.error = "";
self.sendToast("L'activité à été mis à jour");
self.$nextTick(() => {
if (!self.modale) {
throw new Error("modale is undefined");
}
self.modalLoading = false;
self.modale.hide();
activity.id = response.data.id;
self.currentActivity = activity;
});
if (response.data.hasOverlap) {
window.location.reload();
}
})
.catch((error: any) => {
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: LaravelModel,
activity: LaravelModel,
selectedMoment: Moment,
isMorning = true
) {
selectedMoment = selectedMoment.clone();
if (!this.modale) {
throw new Error("modale is undefined");
}
const self = this,
startMoment = activity.start
? this.momentJs(String(activity.start))
: isMorning
? this.getMorningMoment(selectedMoment, true)
: this.getAfterNoonMoment(selectedMoment, true),
endMoment = activity.end
? this.momentJs(String(activity.end))
: isMorning
? this.getMorningMoment(selectedMoment, false)
: this.getAfterNoonMoment(selectedMoment, false);
this.modaleProps = {
activity: activity.activity as number | null,
start: String(
activity.start ?? startMoment.format("YYYY-MM-DDTHH:mm:ss")
),
startMorning: startMoment.format("HH") === "00" ? 1 : 0,
end: String(activity.end ?? endMoment.format("YYYY-MM-DDTHH:mm:ss")),
endMorning: endMoment.format("HH") === "11" ? 1 : 0,
load: Number.parseInt(String(activity.load)),
comment: String(activity.comment ?? ""),
name: String(project.name ?? ""),
project: project,
};
this.currentActivity = activity;
this.modale.show();
setTimeout(() => {
self.$nextTick(self.refreshTooltips);
}, 2000);
},
dayShouldbeenSeen(moment: Moment) {
return moment.day() !== 6 && moment.day() !== 0;
},
projectHasMarkersForTheDay(moment: Moment, project: LaravelModel) {
for (let marker of project.markers as LaravelModelList) {
if (moment.isSame(String(marker.marker), "day")) {
return true;
}
}
return false;
},
setActivityHover(activity: LaravelModel, activate = true): void {
if (!activate) {
this.hoverActivityId = null;
} else {
this.hoverActivityId =
activity.id !== undefined ? String(activity.id) : "";
}
},
// * GETTERS
getLoadPercent(moment: Moment): number {
const load = this.getLoad(moment);
if (load === null) {
return 0;
}
return load > 1 ? 1 : load;
},
getLoad(moment: Moment): number {
if (!this.loadCollection) {
throw new Error("loadCollection is not defined");
}
return this.loadCollection[moment.format("YYYY-MM-DD 00:00:00")] ?? 0;
},
getActivityTooltip(activity: LaravelModel): string {
let activityEnumLabel =
activity.activity !== undefined
? this.activitiesEnum[activity.activity as number]
? this.activitiesEnum[activity.activity as number].label
: ""
: "";
return activity.activity !== undefined
? `${activityEnumLabel}, Charge ${activity.load} personnes, ${activity.comment}`
: "";
},
getActivityClass(activity: LaravelModel): string {
return activity ? `bg-enum-${activity.activity}` : "bg-white";
},
getProject(project_id: number): LaravelModel {
return this.projects[project_id];
},
getActivity(moment: Moment, project: LaravelModel): LaravelModel {
for (let activity of Object.values(
project.activities
) as LaravelModelList) {
if (
moment.isBetween(
this.momentJs(
String(activity.start),
momentJs.HTML5_FMT.DATETIME_LOCAL_SECONDS
),
this.momentJs(
String(activity.end),
momentJs.HTML5_FMT.DATETIME_LOCAL_SECONDS
),
"hour",
"[]"
)
) {
return activity;
}
}
return {};
},
getMorningMoment(moment: Moment, start = true): Moment {
moment = moment.clone();
return start
? moment.hours(0).minutes(0).seconds(0)
: moment.hours(11).minutes(59).seconds(0);
},
getAfterNoonMoment(moment: Moment, start = true): Moment {
moment = moment.clone();
return start
? moment.hours(12).minutes(0).seconds(0)
: moment.hours(23).minutes(59).seconds(0);
},
getMarkersTooltip(moment: Moment, project: LaravelModel): string {
let tooltip = "";
for (let marker of project.markers as LaravelModelList) {
if (moment.isSame(String(marker.marker), "day")) {
tooltip += `${this.momentJs(String(marker.marker)).format(
"DD/MM/YYYY"
)} ${marker.label}<br>`;
}
}
return tooltip;
},
getProjectRoute(project: LaravelModel): string {
const route = this.route("projects.show", {
PROJECT: project.id as number,
});
if (!route) {
throw new Error("undefined route projects.show");
}
return route;
},
// * FILTERS
str_limit(value: string, size = 25) {
if (!value) return "";
value = value.toString();
if (value.length <= size) {
return value;
}
return value.substr(0, size) + "...";
},
ucfirst(value: string) {
return value.charAt(0).toUpperCase() + value.slice(1);
},
// * NAVIGATION
goPreviousWeek(): void {
let moment = this.startMoment ?? this.todayMoment;
moment = this.momentJs(moment.week(), "w");
this.showMacroScheduleDate("", moment.subtract(1, "week"));
},
goNextWeek(): void {
let moment = this.startMoment ?? this.todayMoment;
moment = this.momentJs(moment.week(), "w");
this.showMacroScheduleDate("", moment.add(1, "week"));
},
showMacroScheduleDate(
dateString: string,
moment: Moment | null = null
): void {
const url = new URL(String(window.location));
moment = moment ?? this.momentJs(dateString);
(window as Window).location =
url.origin + url.pathname + "?date=" + moment.format("DD/MM/YYYY");
},
sendToast(message: string): void {
Toastify({
text: message,
duration: 3000,
gravity: "top",
position: "right",
offset: {
x: "2rem",
y: "3rem",
},
style: {
background: "linear-gradient(to right, #00317d, #0058ca)",
},
}).showToast();
},
refreshTooltips(): void {
let exisitingToolips = document.querySelectorAll(
".tooltip[role=\"tooltip\"]"
);
for (let tooltip of exisitingToolips) {
tooltip.remove();
}
this.initTooltips();
},
limitLines(e: KeyboardEvent) {
const comment = this.$refs.comment as HTMLTextAreaElement;
let keynum,
lines = comment.value.split("\n").length;
if (e.key !== undefined) {
keynum = e.key;
} else if (e.keyCode !== undefined) {
// IE
if (window.event) {
keynum = e.keyCode;
// Netscape/Firefox/Opera
} else if (e.which) {
keynum = e.which;
}
}
if (keynum === undefined) {
throw new Error("undefined pressed key");
}
keynum = Number.parseInt(String(keynum));
if (keynum !== 8 && comment.value.length >= this.textLimit) {
e.preventDefault();
return false;
}
if (keynum === 13 && lines == comment.rows) {
e.preventDefault();
return false;
}
},
initTooltips(): void {
// * INIT Bootstrap tooltips
let tooltipTriggerList = [].slice.call(
document.querySelectorAll(
".color-picker-component [data-bs-toggle=\"tooltip\"]"
)
);
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new window.bootstrap.Tooltip(tooltipTriggerEl, {
delay: { show: 1000, hide: 300 },
});
});
},
},
});
</script>
<style lang="scss">
@import "./../../sass/variables";
@import "./../../sass/vendor/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>