1203 lines
33 KiB
Vue
1203 lines
33 KiB
Vue
<template>
|
|
<div class="plannings">
|
|
<div class="row justify-content-center">
|
|
<div class="col-1 text-center actions_col">
|
|
<a
|
|
class="sticky-top btn btn-primary mt-5"
|
|
@click.prevent="goToPlanning(false)"
|
|
>←</a>
|
|
</div>
|
|
|
|
<div class="col-10">
|
|
<table
|
|
class="table table-bordered align-middle text-center schedules_table"
|
|
>
|
|
<thead>
|
|
<tr>
|
|
<th rowspan="2" />
|
|
<th
|
|
:colspan="showWeekends ? 7 : 5"
|
|
v-for="item in weeksCells"
|
|
class="Mon Fri"
|
|
:key="item.num"
|
|
>
|
|
S {{ item.num }} - {{ item.month }} {{ item.year }}
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<th
|
|
v-for="day in days"
|
|
:class="`day${day.eday}`"
|
|
:key="day.num"
|
|
>
|
|
{{ day.day }} {{ day.num }}
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<template v-for="(user, index) in schedules">
|
|
<tr
|
|
v-for="(slot, key) in user"
|
|
:key="`${String(index)} ${String(key)}`"
|
|
>
|
|
<td
|
|
:rowspan="daySlots"
|
|
class="resource_ident"
|
|
v-if="key == 1"
|
|
>
|
|
{{ String(users[index].name) }}
|
|
</td>
|
|
<td
|
|
v-for="day in days"
|
|
:class="'dynCell day ' + String(user[key][day.key as any].type.value) + ' ' + day.eday"
|
|
:key="day.key"
|
|
>
|
|
<div
|
|
:class="
|
|
'cellsizer ' +
|
|
(user[key][day.key as any].type.value
|
|
? String(user[key][day.key as any].type.value)
|
|
: 'empty')
|
|
"
|
|
>
|
|
<b v-if="user[key][day.key as any]['model_lie']['value']">
|
|
<a
|
|
target="_blank"
|
|
class="text-light bg-dark"
|
|
:href="String(getCellObjectInfo(user[key][day.key as any])?.url)"
|
|
>
|
|
{{
|
|
String(user[key][day.key as any]["id_lie"]["label"])
|
|
}}
|
|
</a> </b><br>
|
|
{{ String(user[key][day.key as any]["text"]) }}
|
|
|
|
<button
|
|
class="btn btn-edit"
|
|
type="button"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#editModal"
|
|
@click.prevent="
|
|
setModalData(
|
|
index,
|
|
key,
|
|
day.key,
|
|
user[key][day.key as any]['id']
|
|
)
|
|
"
|
|
>
|
|
✎
|
|
</button>
|
|
<button
|
|
:class="'btn btn-paste ' + (!clipBoard.set ? 'hide' : '')"
|
|
type="button"
|
|
@click.prevent="
|
|
pasteCell(
|
|
index,
|
|
key,
|
|
day.key,
|
|
user[key][day.key as any]['id']
|
|
)
|
|
"
|
|
>
|
|
📋
|
|
</button>
|
|
<button
|
|
class="btn btn-copy"
|
|
type="button"
|
|
@click.prevent="
|
|
copyCell(
|
|
index,
|
|
key,
|
|
day.key,
|
|
user[key][day.key as any]['id']
|
|
)
|
|
"
|
|
>
|
|
<span
|
|
style="
|
|
font-size: 0.875em;
|
|
margin-right: 0.125em;
|
|
position: relative;
|
|
top: -0.25em;
|
|
left: -0.125em;
|
|
"
|
|
>
|
|
📄
|
|
<span
|
|
style="position: absolute; top: 0.25em; left: 0.25em"
|
|
>
|
|
📄
|
|
</span>
|
|
</span>
|
|
</button>
|
|
<button
|
|
:class="
|
|
'btn btn-clear ' + (!user[key][day.key as any]['id'] ? 'hide' : '')
|
|
"
|
|
@click.prevent="
|
|
eraseCell(
|
|
index,
|
|
key,
|
|
day.key,
|
|
user[key][day.key as any]['id']
|
|
)
|
|
"
|
|
>
|
|
⌫
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="col-1 text-center actions_col">
|
|
<a
|
|
class="sticky-top btn btn-primary mt-5"
|
|
@click="goToPlanning(true)"
|
|
>→</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal -->
|
|
<div
|
|
class="modal fade"
|
|
id="editModal"
|
|
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"
|
|
>
|
|
{{ modale.title }}
|
|
</h5>
|
|
<button
|
|
ref="close"
|
|
type="button"
|
|
class="btn-close"
|
|
data-bs-dismiss="modal"
|
|
aria-label="Close"
|
|
/>
|
|
</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"
|
|
>
|
|
Categorie
|
|
</label>
|
|
<select
|
|
id="categorie"
|
|
ref="type"
|
|
class="form-select"
|
|
aria-label="Sélectionnez une catégorie"
|
|
>
|
|
<option value="">
|
|
Sélectionnez une catégorie
|
|
</option>
|
|
<option
|
|
v-for="(activity, index) of activities"
|
|
:key="index"
|
|
:value="activity.value"
|
|
:selected="modale.activity.value == activity.value"
|
|
:class="activity.value"
|
|
>
|
|
{{ String(activity.label) }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group mb-3">
|
|
<label
|
|
for="morph"
|
|
class="col-form-label text-md-right ms-5"
|
|
>
|
|
Type d'élément lié
|
|
</label>
|
|
<select
|
|
id="morph"
|
|
ref="morph"
|
|
class="form-select"
|
|
@change="selectChange"
|
|
aria-label="Sélectionnez un élément à lier"
|
|
>
|
|
<option value="">
|
|
Sélectionnez un type d'objet à lier
|
|
</option>
|
|
<option
|
|
v-for="(model, index) of models"
|
|
:key="index"
|
|
:value="model.value"
|
|
:selected="modale.model_lie.value == model.value"
|
|
>
|
|
{{ String(model.label) }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<div
|
|
class="form-group mb-3"
|
|
v-if="modale.dDisplay"
|
|
>
|
|
<label
|
|
for="select_id_lie"
|
|
class="col-form-label text-md-right ms-5"
|
|
>
|
|
Élément lié
|
|
</label>
|
|
<VueSelect
|
|
id="select_id_lie"
|
|
ref="select"
|
|
:loading="loading"
|
|
v-model="value"
|
|
:clearable="false"
|
|
label="label"
|
|
:options="data"
|
|
@search="search"
|
|
:disabled="dReadonly || dDisabled"
|
|
:filterable="true"
|
|
:searchable="true"
|
|
@input="setSelected"
|
|
>
|
|
<li
|
|
slot="list-footer"
|
|
class="pagination"
|
|
>
|
|
<button
|
|
@click.prevent="
|
|
$event.preventDefault();
|
|
page -= 1;
|
|
"
|
|
:disabled="!hasPrevPage"
|
|
class="btn btn-sm btn-primary mt-2 mb-2 ms-2 me-2"
|
|
>
|
|
Précédent
|
|
</button>
|
|
<button
|
|
@click.prevent="
|
|
$event.preventDefault();
|
|
page += 1;
|
|
"
|
|
:disabled="!hasNextPage"
|
|
class="btn btn-sm btn-primary mt-2 mb-2"
|
|
>
|
|
Suivant
|
|
</button>
|
|
</li>
|
|
</VueSelect>
|
|
</div>
|
|
<div class="form-group mb-3">
|
|
<label
|
|
for="text"
|
|
class="col-form-label text-md-right ms-5"
|
|
>
|
|
Description courte
|
|
</label>
|
|
<textarea
|
|
ref="comment"
|
|
class="form-control"
|
|
name="text"
|
|
id="text"
|
|
rows="2"
|
|
placeholder=""
|
|
@keydown="limitLines($event)"
|
|
v-model="modale.text"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row justify-content-center">
|
|
<div class="col-8 text-center">
|
|
<button
|
|
class="btn btn-primary me-3"
|
|
@click.prevent="saveCell()"
|
|
>
|
|
Valider
|
|
</button>
|
|
<button
|
|
class="btn btn-danger"
|
|
@click.prevent="($refs.close as HTMLButtonElement).click()"
|
|
>
|
|
Fermer
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import axios from "axios";
|
|
import VueSelect from "vue-select";
|
|
import { defineComponent } from "vue";
|
|
import * as Toastify from "toastify-js";
|
|
import route from "../modules/route";
|
|
import trans from "../modules/trans";
|
|
import * as momentJs from "moment";
|
|
|
|
type Lie = {
|
|
label: string | null;
|
|
value: number | string | null;
|
|
};
|
|
|
|
type Day = {
|
|
day: string;
|
|
num: string;
|
|
eday: string;
|
|
key: string;
|
|
};
|
|
|
|
type DaySlot = {
|
|
id: number;
|
|
type: Lie;
|
|
text: string;
|
|
id_lie: Lie;
|
|
model_lie: Lie;
|
|
};
|
|
|
|
type SchedDay = {
|
|
id: number | null;
|
|
type: Lie;
|
|
text: string;
|
|
id_lie: Lie;
|
|
model_lie: Lie;
|
|
};
|
|
type Modale = {
|
|
id: number;
|
|
userid: number;
|
|
slot: number;
|
|
day: string;
|
|
title: string;
|
|
dDisplay: boolean;
|
|
text: string;
|
|
activity: Lie;
|
|
model_lie: Lie;
|
|
id_lie: Lie;
|
|
};
|
|
|
|
type ServerData = {
|
|
id: number;
|
|
user_id: number;
|
|
date: string;
|
|
activity: Lie;
|
|
slot: number;
|
|
model_lie: Lie;
|
|
id_lie: Lie;
|
|
description: string;
|
|
};
|
|
|
|
type ClipBoardType = {
|
|
set: boolean;
|
|
type: Lie;
|
|
text: string;
|
|
id_lie: Lie | null;
|
|
model_lie: Lie | null;
|
|
user_id: number | null;
|
|
slot: number | null;
|
|
activity: Lie | null;
|
|
id: number | null;
|
|
description: string;
|
|
date: string;
|
|
};
|
|
|
|
export default defineComponent({
|
|
mixins: [trans, route, { methods: { momentJs } }],
|
|
components: {
|
|
VueSelect,
|
|
},
|
|
mounted() {
|
|
window.Toastify = Toastify;
|
|
|
|
const json = String(this.$attrs.json ?? "{}"),
|
|
data = JSON.parse(json);
|
|
|
|
this.daySlots = !Number.isNaN(Number.parseInt(data.daySlots))
|
|
? Number.parseInt(data.daySlots)
|
|
: 4;
|
|
this.showWeekends = data.daySlots == "true" ? true : false;
|
|
this.currentDate = data.browseDate ?? new Date();
|
|
this.weeksCells = data.weeks;
|
|
this.numweeks = this.weeksCells.length;
|
|
this.days = this.prepareDays();
|
|
this.users = data.users;
|
|
this.schedules = this.prepareSched(data.schedules);
|
|
this.activities = data.activities;
|
|
this.models = data.models;
|
|
},
|
|
data(): {
|
|
showWeekends: boolean;
|
|
daySlots: number;
|
|
numweeks: number;
|
|
currentDate: null;
|
|
weeksCells: {
|
|
num: number;
|
|
month: number;
|
|
year: number;
|
|
}[];
|
|
days: Day[];
|
|
users: LaravelModelList;
|
|
schedules: Record<number, Record<number, DaySlot[]>>;
|
|
activities: LaravelModelList;
|
|
models: LaravelModelList;
|
|
error: string;
|
|
modale: Modale;
|
|
//v-select de ses morts
|
|
dDisabled: boolean;
|
|
dReadonly: boolean;
|
|
loading: boolean;
|
|
value: Lie;
|
|
searchText: string;
|
|
data: [];
|
|
page: number;
|
|
perPage: number;
|
|
hasNextPage: boolean;
|
|
hasPrevPage: boolean;
|
|
emptyCell: SchedDay;
|
|
clipBoard: ClipBoardType;
|
|
} {
|
|
return {
|
|
showWeekends: false,
|
|
daySlots: 4,
|
|
numweeks: 0,
|
|
currentDate: null,
|
|
weeksCells: [],
|
|
days: [],
|
|
users: [],
|
|
schedules: [],
|
|
activities: [],
|
|
models: [],
|
|
error: "",
|
|
modale: {
|
|
id: 0,
|
|
userid: 0,
|
|
slot: 0,
|
|
day: "01/01/1900",
|
|
title: "",
|
|
dDisplay: false,
|
|
text: "",
|
|
activity: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
model_lie: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
id_lie: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
},
|
|
//v-select de ses morts
|
|
dDisabled: false,
|
|
dReadonly: false,
|
|
loading: false,
|
|
value: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
searchText: "",
|
|
data: [],
|
|
page: 1,
|
|
perPage: 15,
|
|
hasNextPage: true,
|
|
hasPrevPage: false,
|
|
emptyCell: {
|
|
type: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
text: "",
|
|
id_lie: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
model_lie: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
id: null,
|
|
},
|
|
clipBoard: {
|
|
set: false,
|
|
type: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
text: "",
|
|
id_lie: null,
|
|
model_lie: null,
|
|
id: null,
|
|
user_id: null,
|
|
slot: null,
|
|
description: "",
|
|
date: "",
|
|
activity: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
},
|
|
};
|
|
},
|
|
watch: {
|
|
page() {
|
|
if (this.page < 1) {
|
|
this.page = 1;
|
|
}
|
|
if (this.page > 1) {
|
|
this.hasPrevPage = true;
|
|
} else {
|
|
this.hasPrevPage = false;
|
|
}
|
|
this.search();
|
|
},
|
|
data() {
|
|
if (this.data.length == this.perPage) {
|
|
this.hasNextPage = true;
|
|
} else {
|
|
this.hasNextPage = false;
|
|
}
|
|
},
|
|
},
|
|
methods: {
|
|
sendToast(message: string) {
|
|
Toastify({
|
|
text: message,
|
|
duration: 3000,
|
|
gravity: "top",
|
|
position: "right",
|
|
offset: {
|
|
x: "2rem",
|
|
y: "3rem",
|
|
},
|
|
style: {
|
|
background: "linear-gradient(to right, #00317d, #0058ca)",
|
|
},
|
|
}).showToast();
|
|
},
|
|
goToPlanning(forward: boolean): void {
|
|
const route = this.route("schedule.index");
|
|
if (!route) {
|
|
throw new Error("undefined route schedule.index");
|
|
}
|
|
// console.log(forward);
|
|
let date = this.momentJs(this.currentDate)
|
|
.startOf("week")
|
|
.subtract(7, "day");
|
|
if (forward) {
|
|
date = this.momentJs(this.currentDate).add(7, "day");
|
|
}
|
|
const url = new URL(route);
|
|
(window as Window).location =
|
|
url.origin + url.pathname + "?p=" + date.format("DD/MM/YYYY");
|
|
},
|
|
prepareDays(): Day[] {
|
|
let mydays = [];
|
|
for (let i = 0; i < this.numweeks * 7; i++) {
|
|
const date = this.momentJs(this.currentDate).add(i, "day"),
|
|
obj = {
|
|
day: date.format("ddd"),
|
|
num: date.format("DD"),
|
|
eday: date.locale("en").format("ddd"),
|
|
key: date.format("DD/MM/YYYY"),
|
|
};
|
|
mydays.push(obj);
|
|
if (!this.showWeekends) {
|
|
if (date.day() == 5) {
|
|
i += 2;
|
|
}
|
|
}
|
|
}
|
|
return mydays;
|
|
},
|
|
prepareSched(
|
|
dbscheds: Record<number, Record<number, DaySlot[]>>
|
|
): Record<number, Record<number, DaySlot[]>> {
|
|
const templateObj: SchedDay = {
|
|
type: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
text: "",
|
|
id_lie: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
model_lie: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
id: null,
|
|
};
|
|
//we need to make missing users
|
|
Object.keys(this.users).forEach((id) => {
|
|
if (dbscheds[Number.parseInt(id)] === undefined) {
|
|
dbscheds[Number.parseInt(id)] = {};
|
|
}
|
|
});
|
|
Object.keys(dbscheds).forEach((userid) => {
|
|
//what if we missing a slot for the whole period ?
|
|
//we need to make missing slots too
|
|
for (let i = 1; i < this.daySlots + 1; i++) {
|
|
if (dbscheds[Number.parseInt(userid)][i] === undefined) {
|
|
dbscheds[Number.parseInt(userid)][i] = [];
|
|
}
|
|
}
|
|
Object.keys(dbscheds[Number.parseInt(userid)]).forEach((slotkey) => {
|
|
this.days.forEach((day) => {
|
|
const schedSlot = dbscheds[Number.parseInt(userid)][
|
|
Number.parseInt(slotkey)
|
|
] as SchedDay[];
|
|
if (schedSlot[day.key as any] === undefined) {
|
|
//console.log('need ' + day.key + ' for slot ' + slotkey + ' for user ' + userid);
|
|
schedSlot[day.key as any] = templateObj;
|
|
}
|
|
});
|
|
});
|
|
});
|
|
return dbscheds;
|
|
},
|
|
getCellObjectInfo(cellData: DaySlot) {
|
|
for (let model of this.models) {
|
|
if (
|
|
cellData["model_lie"].value === model.value &&
|
|
model.value == "tickets"
|
|
) {
|
|
return {
|
|
url: this.route("ticket.show", {
|
|
TICKET: String(cellData["id_lie"].value),
|
|
}),
|
|
text: cellData["model_lie"].label,
|
|
};
|
|
}
|
|
if (
|
|
cellData["model_lie"].value === model.value &&
|
|
model.value == "sites"
|
|
) {
|
|
return {
|
|
url: this.route("sites.show", {
|
|
SITE: String(cellData["id_lie"].value),
|
|
}),
|
|
text: cellData["model_lie"].label,
|
|
};
|
|
}
|
|
if (
|
|
cellData["model_lie"].value === model.value &&
|
|
model.value == "projects"
|
|
) {
|
|
return {
|
|
url: this.route("projects.show", {
|
|
PROJECT: String(cellData["id_lie"].value),
|
|
}),
|
|
text: cellData["model_lie"].label,
|
|
};
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
resetModalFields() {
|
|
this.value = {
|
|
label: "",
|
|
value: 0,
|
|
};
|
|
(this.$refs.type as HTMLSelectElement).value = "";
|
|
(this.$refs.morph as HTMLSelectElement).value = "";
|
|
},
|
|
setModalData(userid: number, slotkey: number, day: string, id: number) {
|
|
let text = this.schedules[userid][slotkey][day as any]["text"],
|
|
activity = this.schedules[userid][slotkey][day as any]["type"],
|
|
model = this.schedules[userid][slotkey][day as any]["model_lie"],
|
|
enumobject = {
|
|
label: "",
|
|
value: "",
|
|
},
|
|
currentModale: Modale = {
|
|
id: this.schedules[userid][slotkey][day as any]["id"],
|
|
userid: userid,
|
|
slot: slotkey,
|
|
day: day,
|
|
title: String(id),
|
|
dDisplay: false,
|
|
text: text,
|
|
activity: activity,
|
|
model_lie: model ?? enumobject,
|
|
id_lie: this.schedules[userid][slotkey][day as any]["id_lie"],
|
|
};
|
|
this.modale = currentModale;
|
|
this.$nextTick(() => {
|
|
if (this.modale.id_lie.value === null) {
|
|
throw new Error(" modale.id_lie.value is null");
|
|
}
|
|
let route = null;
|
|
switch (this.modale.model_lie.value) {
|
|
case "tickets":
|
|
this.modale.dDisplay = true;
|
|
route = this.route("ticket.show", {
|
|
TICKET: this.modale.id_lie.value,
|
|
});
|
|
break;
|
|
case "projects":
|
|
this.modale.dDisplay = true;
|
|
route = this.route("projects.show", {
|
|
PROJECT: this.modale.id_lie.value,
|
|
});
|
|
break;
|
|
case "sites":
|
|
this.modale.dDisplay = true;
|
|
route = this.route("sites.show", {
|
|
SITE: this.modale.id_lie.value,
|
|
});
|
|
break;
|
|
default:
|
|
this.modale.dDisplay = false;
|
|
}
|
|
this.selectChange();
|
|
this.value = this.schedules[userid][slotkey][day as any]["id_lie"];
|
|
});
|
|
},
|
|
pasteCell(userid: number, slotkey: number, day: string, id: number) {
|
|
if (this.clipBoard.activity === null) {
|
|
throw new Error("clipBoard activity is null");
|
|
}
|
|
if (this.clipBoard.model_lie === null) {
|
|
throw new Error("clipBoard model_lie is null");
|
|
}
|
|
if (this.clipBoard.id_lie === null) {
|
|
throw new Error("clipBoard id_lie is null");
|
|
}
|
|
//this.eraseCell(userid,slotkey,day,id)
|
|
const data: ServerData = {
|
|
id: id,
|
|
user_id: userid,
|
|
date: day,
|
|
activity: this.clipBoard.activity,
|
|
slot: slotkey,
|
|
model_lie: this.clipBoard.model_lie,
|
|
id_lie: this.clipBoard.id_lie,
|
|
description: this.clipBoard.description,
|
|
};
|
|
this.saveCellOnServer(data);
|
|
//do not clear clipboard
|
|
//this.clearClipBoard()
|
|
this.sendToast("Cellule collée");
|
|
},
|
|
clearClipBoard() {
|
|
this.clipBoard = {
|
|
set: false,
|
|
type: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
text: "",
|
|
activity: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
id_lie: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
model_lie: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
id: null,
|
|
user_id: null,
|
|
slot: null,
|
|
description: "",
|
|
date: "",
|
|
};
|
|
},
|
|
copyCell(userid: number, slotkey: number, day: string, id: number) {
|
|
this.clipBoard = {
|
|
set: true,
|
|
id: id,
|
|
user_id: userid,
|
|
date: day,
|
|
activity: this.schedules[userid][slotkey][day as any].type,
|
|
slot: slotkey,
|
|
model_lie: this.schedules[userid][slotkey][day as any].model_lie,
|
|
id_lie: this.schedules[userid][slotkey][day as any].id_lie,
|
|
description: this.schedules[userid][slotkey][day as any].text,
|
|
text: "",
|
|
type: {
|
|
label: "",
|
|
value: 0,
|
|
},
|
|
};
|
|
|
|
this.sendToast("Cellule copiée");
|
|
},
|
|
setSelected(value: Lie) {
|
|
this.modale.id_lie = value;
|
|
},
|
|
search(search: string | false | null = null) {
|
|
this.$nextTick(() => {
|
|
this.error = "";
|
|
let route;
|
|
switch ((this.$refs.morph as HTMLSelectElement).value) {
|
|
case "tickets":
|
|
route = this.route("ticket.index");
|
|
break;
|
|
case "projects":
|
|
route = this.route("projects.index");
|
|
break;
|
|
case "sites":
|
|
route = this.route("sites.index");
|
|
break;
|
|
}
|
|
if (route === null || route === undefined) {
|
|
throw new Error(
|
|
" unhandled select " + (this.$refs.morph as HTMLSelectElement).value
|
|
);
|
|
}
|
|
if (search !== false && search !== null) {
|
|
this.searchText = search;
|
|
}
|
|
route += "?page=" + this.page;
|
|
route += "&s=" + this.searchText;
|
|
this.loading = true;
|
|
axios
|
|
.get(route)
|
|
.then((response) => {
|
|
this.data = response.data;
|
|
})
|
|
.catch((error: any) => {
|
|
if (error.response.status === 429) {
|
|
location.reload();
|
|
}
|
|
this.error = error;
|
|
})
|
|
.then(() => {
|
|
this.loading = false;
|
|
});
|
|
});
|
|
},
|
|
saveCell() {
|
|
this.error = "";
|
|
const data: ServerData = {
|
|
id: this.modale.id,
|
|
user_id: this.modale.userid,
|
|
date: this.modale.day,
|
|
activity: {
|
|
label: String((this.$refs.type as HTMLSelectElement).value),
|
|
value: (this.$refs.type as HTMLSelectElement).value,
|
|
},
|
|
slot: this.modale.slot,
|
|
model_lie: {
|
|
label: String((this.$refs.morph as HTMLSelectElement).value),
|
|
value: (this.$refs.morph as HTMLSelectElement).value,
|
|
},
|
|
id_lie: this.value,
|
|
description: (this.$refs.comment as HTMLTextAreaElement).value,
|
|
};
|
|
this.saveCellOnServer(data);
|
|
},
|
|
saveCellOnServer(data: ServerData) {
|
|
this.error = "";
|
|
const self = this,
|
|
routeUri = self.route("schedule.setCellContent");
|
|
if (self.error.length) {
|
|
return;
|
|
}
|
|
|
|
if (!routeUri) {
|
|
throw new Error("schedule.setCellContent is undefined");
|
|
}
|
|
axios
|
|
.post(routeUri, {
|
|
id: data.id,
|
|
user_id: data.user_id,
|
|
date: data.date,
|
|
activity: data.activity.value,
|
|
slot: data.slot,
|
|
model_lie: data.model_lie.value,
|
|
id_lie: data.id_lie.value,
|
|
description: data.description,
|
|
})
|
|
.then((response) => {
|
|
if (response.data.status) {
|
|
const sched = Object.assign({}, response.data.sched);
|
|
// sched.date = Date.parse(sched.date)
|
|
// sched.date = this.moment(sched.date).format('DD/MM/YYYY');
|
|
let cellData = {
|
|
type: sched.activity,
|
|
text: sched.description,
|
|
id_lie: sched.id_lie,
|
|
model_lie: sched.model_lie,
|
|
id: sched.id,
|
|
};
|
|
this.schedules[sched.user_id][sched.slot][sched.date] = cellData;
|
|
(this.$refs.close as HTMLButtonElement).click();
|
|
} else {
|
|
this.error = "Réponse inconnue";
|
|
}
|
|
})
|
|
.catch((error: any) => {
|
|
console.log(error);
|
|
if (error.response.status === 429) {
|
|
location.reload();
|
|
}
|
|
if (error.response.status === 422) {
|
|
if (error.response.data.errors) {
|
|
this.error = "";
|
|
for (let id in error.response.data.errors) {
|
|
let field = error.response.data.errors[id];
|
|
this.error += field.join("\n") + "\n";
|
|
}
|
|
return;
|
|
} else if (error.response.data.message) {
|
|
this.error = error.response.data.message;
|
|
return;
|
|
}
|
|
}
|
|
this.error = error;
|
|
});
|
|
},
|
|
eraseCell(userid: number, slotkey: number, day: string, id: number) {
|
|
const route = this.route("schedule.eraseCellContent"),
|
|
data = {
|
|
id: id,
|
|
};
|
|
if (!route) {
|
|
throw new Error("undefined route schedule.eraseCellContent");
|
|
}
|
|
axios
|
|
.post(route, data)
|
|
.then((response) => {
|
|
if (response.data.status) {
|
|
this.schedules[userid][slotkey][day as any] = this
|
|
.emptyCell as DaySlot;
|
|
} else {
|
|
console.error("Unknown response");
|
|
}
|
|
})
|
|
.catch((error: any) => {
|
|
if (error.response.status === 429) {
|
|
location.reload();
|
|
}
|
|
console.error(error);
|
|
});
|
|
},
|
|
selectChange() {
|
|
this.value = {
|
|
label: null,
|
|
value: null,
|
|
};
|
|
switch ((this.$refs.morph as HTMLSelectElement).value) {
|
|
case "tickets":
|
|
case "projects":
|
|
case "sites":
|
|
this.modale.dDisplay = true;
|
|
this.search(false);
|
|
break;
|
|
default:
|
|
this.modale.dDisplay = false;
|
|
}
|
|
},
|
|
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;
|
|
}
|
|
},
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "vue-select/src/scss/vue-select.scss";
|
|
|
|
.actions_col {
|
|
width: 45px;
|
|
padding: 0;
|
|
}
|
|
|
|
.plannings {
|
|
.sticky-top {
|
|
top: 20rem !important;
|
|
}
|
|
}
|
|
|
|
table {
|
|
.Mon {
|
|
border-left: 4px solid;
|
|
}
|
|
// .Fri {
|
|
// border-right: 4px solid;
|
|
// }
|
|
}
|
|
|
|
table.schedules_table {
|
|
table-layout: fixed;
|
|
|
|
.resource_ident {
|
|
font-weight: bold;
|
|
width: 150px;
|
|
}
|
|
|
|
.cellsizer {
|
|
height: 50px;
|
|
text-overflow: ellipsis;
|
|
overflow: hidden;
|
|
b {
|
|
width: 90%;
|
|
}
|
|
}
|
|
}
|
|
|
|
.dynCell {
|
|
position: relative;
|
|
&.day {
|
|
//position: relative;
|
|
&:hover .btn {
|
|
opacity: 1;
|
|
}
|
|
&:hover .cellsizer {
|
|
height: 140px;
|
|
padding-top: 2rem;
|
|
position: absolute;
|
|
width: 200%;
|
|
left: -50%;
|
|
top: -55%;
|
|
z-index: 20;
|
|
border: 1px solid;
|
|
}
|
|
$buttonBgColor: #4d4d4d;
|
|
|
|
// 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 {
|
|
position: absolute;
|
|
top: 0.1rem;
|
|
left: 0.1rem;
|
|
padding: 0;
|
|
width: 1.8rem;
|
|
height: 1.8rem;
|
|
margin: 0;
|
|
background-color: $buttonBgColor;
|
|
opacity: 0;
|
|
&.hide {
|
|
opacity: 0 !important;
|
|
}
|
|
&:hover,
|
|
&:focus {
|
|
background-color: darken($buttonBgColor, 10%);
|
|
}
|
|
&.btn-clear {
|
|
color: darken($color: #fff, $amount: 5);
|
|
}
|
|
&:nth-last-child(2) {
|
|
top: 0.1rem;
|
|
right: inherit;
|
|
left: 2.1rem;
|
|
}
|
|
&:nth-last-child(3) {
|
|
right: inherit;
|
|
top: 0.1rem;
|
|
left: 4.1rem;
|
|
}
|
|
&:nth-last-child(1) {
|
|
top: 0.1rem;
|
|
left: calc(100% - 1.9rem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@import "toastify-js/src/toastify.css";
|
|
@import "vue-select/src/scss/vue-select.scss";
|
|
.planning {
|
|
overflow: auto;
|
|
|
|
td {
|
|
height: 50px;
|
|
}
|
|
|
|
#text {
|
|
height: 5.5rem;
|
|
padding: 1rem 2rem;
|
|
resize: none;
|
|
width: 37rem;
|
|
margin: 0 auto;
|
|
}
|
|
}
|
|
</style>
|