|
|
@@ -1,17 +1,19 @@
|
|
|
<template>
|
|
|
<div class="header">
|
|
|
- <div class="logo"></div>
|
|
|
- <h1 class="appname">BDLG planner</h1>
|
|
|
+ <div class="logo" />
|
|
|
+ <router-link to="/planner" class="appname">BDLG planner</router-link>
|
|
|
<nav class="tabs floating">
|
|
|
- <router-link class="tab" active-class="selected" to="/"> Accueil </router-link>
|
|
|
- <router-link class="tab" active-class="selected" to="/evenement">Planning</router-link>
|
|
|
- <router-link class="tab" active-class="selected" to="/competences">
|
|
|
+ <router-link class="tab" active-class="selected" to="/planner/evenement">
|
|
|
+ Accueil
|
|
|
+ </router-link>
|
|
|
+ <router-link class="tab" active-class="selected" to="/planner/planning">Planning</router-link>
|
|
|
+ <router-link class="tab" active-class="selected" to="/planner/competences">
|
|
|
Gestion des compétences
|
|
|
</router-link>
|
|
|
- <router-link class="tab" active-class="selected" to="/benevoles">
|
|
|
+ <router-link class="tab" active-class="selected" to="/planner/benevoles">
|
|
|
Gestion des bénévoles
|
|
|
</router-link>
|
|
|
- <router-link class="tab" active-class="selected" to="/planningIndividuel">
|
|
|
+ <router-link class="tab" active-class="selected" to="/planner/planningIndividuel">
|
|
|
Planning Individuel
|
|
|
</router-link>
|
|
|
</nav>
|
|
|
@@ -21,7 +23,9 @@
|
|
|
@export="exportStateToJson"
|
|
|
@import="(e) => importJsonState(e, false)"
|
|
|
@localSave="localSave"
|
|
|
+ @save="save"
|
|
|
@solve="solve"
|
|
|
+ @newEvenement="newEvenement"
|
|
|
/>
|
|
|
<div v-if="optimisationInProgress" class="modal">
|
|
|
<div class="modal-content">
|
|
|
@@ -53,11 +57,15 @@ import {
|
|
|
HardConstraint,
|
|
|
} from "./models/SolverInput";
|
|
|
import Ressource, { IRessource, RessourceJSON } from "jc-timeline/lib/Ressource";
|
|
|
+import updatePlanningVersions from "@/mixins/updatePlanningVersions";
|
|
|
import { MutationTypes } from "./store/Mutations";
|
|
|
import dayjs from "dayjs";
|
|
|
import { StateJSON } from "./store/State";
|
|
|
+const APIAdress = "http://localhost:8080/";
|
|
|
+
|
|
|
const keyofEvent: Array<keyof Evenement> = ["name", "uuid", "start", "end"];
|
|
|
export default defineComponent({
|
|
|
+ mixins: [updatePlanningVersions],
|
|
|
data() {
|
|
|
return {
|
|
|
optimisationInProgress: false,
|
|
|
@@ -68,10 +76,22 @@ export default defineComponent({
|
|
|
},
|
|
|
mounted() {
|
|
|
const previousState = window.localStorage.getItem("activeState");
|
|
|
- toast({ html: "coucou" });
|
|
|
+ toast({ html: "Bienvenue" });
|
|
|
if (previousState) {
|
|
|
this.importJsonState(JSON.parse(previousState), false);
|
|
|
+ this.$router.push({ name: "Main" });
|
|
|
}
|
|
|
+ this.$nextTick(() =>
|
|
|
+ fetch(`${APIAdress}api/evenements`)
|
|
|
+ .then((response) => {
|
|
|
+ if (response.status == 200) {
|
|
|
+ return response.json();
|
|
|
+ } else {
|
|
|
+ throw new Error(response.statusText);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .then((data) => this.$store.commit(MutationTypes.refreshSavedPlanning, data))
|
|
|
+ );
|
|
|
window.onbeforeunload = () => {
|
|
|
this.localSave();
|
|
|
};
|
|
|
@@ -80,12 +100,75 @@ export default defineComponent({
|
|
|
localSave() {
|
|
|
window.localStorage.setItem("activeState", JSON.stringify(this.$store.getters.getJSONState));
|
|
|
},
|
|
|
+ save() {
|
|
|
+ const body = {
|
|
|
+ uuid: this.$store.state.evenement.uuid,
|
|
|
+ name: this.$store.state.evenement.name,
|
|
|
+ content: JSON.stringify(this.$store.getters.getJSONState),
|
|
|
+ };
|
|
|
+ // local save
|
|
|
+ window.localStorage.setItem("activeState", body.content);
|
|
|
+ fetch(`${APIAdress}api/evenements/${body.uuid}`, {
|
|
|
+ method: "PUT",
|
|
|
+ body: JSON.stringify(body),
|
|
|
+ headers: {
|
|
|
+ "Content-Type": "application/json",
|
|
|
+ },
|
|
|
+ })
|
|
|
+ .then((response) => {
|
|
|
+ if (response.status == 200) {
|
|
|
+ toast({ html: "Données sauvegardées", classes: "success" });
|
|
|
+ return response.json();
|
|
|
+ } else if (response.status == 404) {
|
|
|
+ toast({
|
|
|
+ html:
|
|
|
+ "Erreur: Le planning n'existe pas dans la base de données.<br>Création du planning ",
|
|
|
+ classes: "error",
|
|
|
+ displayLength: 5000,
|
|
|
+ });
|
|
|
+ return fetch(`${APIAdress}api/evenements/`, {
|
|
|
+ method: "POST",
|
|
|
+ body: JSON.stringify(body),
|
|
|
+ headers: {
|
|
|
+ "Content-Type": "application/json",
|
|
|
+ },
|
|
|
+ }).then((response) => {
|
|
|
+ if (response.status == 200) {
|
|
|
+ toast({ html: "Données sauvegardées", classes: "success", displayLength: 5000 });
|
|
|
+ return response.json();
|
|
|
+ } else {
|
|
|
+ toast({
|
|
|
+ html: "Erreur: Les données n'ont pas été sauvegardées",
|
|
|
+ classes: "error",
|
|
|
+ displayLength: 5000,
|
|
|
+ });
|
|
|
+ throw new Error(response.statusText);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ throw new Error(response.statusText);
|
|
|
+ })
|
|
|
+ .then((data) => this.$store.commit(MutationTypes.newVersion, data))
|
|
|
+ .catch((err) => toast({ html: err }));
|
|
|
+ },
|
|
|
clearToast() {
|
|
|
if (this.lastToast) {
|
|
|
this.lastToast.dismiss();
|
|
|
this.lastToast = null;
|
|
|
}
|
|
|
},
|
|
|
+ newEvenement() {
|
|
|
+ const newState: StateJSON = {
|
|
|
+ evenement: new Evenement().toJSON(),
|
|
|
+ competences: [],
|
|
|
+ benevoles: [],
|
|
|
+ creneaux: [],
|
|
|
+ creneauGroups: [],
|
|
|
+ };
|
|
|
+ newState.evenement.name = "Nouvel événement";
|
|
|
+ this.save();
|
|
|
+ this.importJsonState(newState);
|
|
|
+ },
|
|
|
exportStateToJson(): void {
|
|
|
const obj: StateJSON = this.$store.getters.getJSONState;
|
|
|
const mimeType = "data:text/json;charset=utf-8";
|
|
|
@@ -127,8 +210,14 @@ export default defineComponent({
|
|
|
headers: { "Content-type": "application/json; charset=UTF-8" },
|
|
|
};
|
|
|
this.optimisationInProgress = true;
|
|
|
- fetch(`http://localhost:8080/planning/solve`, options)
|
|
|
- .then((response) => response.json())
|
|
|
+ fetch(`${APIAdress}planning/solve`, options)
|
|
|
+ .then((response) => {
|
|
|
+ if (response.status == 200) {
|
|
|
+ return response.json();
|
|
|
+ } else {
|
|
|
+ throw new Error(response.statusText);
|
|
|
+ }
|
|
|
+ })
|
|
|
.then(this.updatePlanningWithNewPairing)
|
|
|
.catch((error) => {
|
|
|
toast({ html: "Pas de réponse du serveur<br>" + error.toString(), classes: "error" });
|
|
|
@@ -143,7 +232,12 @@ export default defineComponent({
|
|
|
classes: "success",
|
|
|
displayLength: 10000,
|
|
|
});
|
|
|
- if (data.message) toast({ html: data.message, classes: "warning", displayLength: 10000 });
|
|
|
+ if (data.message)
|
|
|
+ toast({
|
|
|
+ html: data.message.replaceAll("\n", "<br>"),
|
|
|
+ classes: "warning",
|
|
|
+ displayLength: 10000,
|
|
|
+ });
|
|
|
this.displayExplanation(data.explanation);
|
|
|
|
|
|
// Remove previous timeslot assignement
|
|
|
@@ -188,20 +282,23 @@ export default defineComponent({
|
|
|
}
|
|
|
this.explanation = scoreExplanationHTML;
|
|
|
this.clearToast();
|
|
|
- this.lastToast = toast({
|
|
|
- html: `<span>Ouvrir l'explication du score</span><i class="material-icons">close<i>`,
|
|
|
- displayLength: Infinity,
|
|
|
- classes: "action",
|
|
|
- });
|
|
|
- this.lastToast.element.firstElementChild?.addEventListener(
|
|
|
- "click",
|
|
|
- () => (this.showExplanation = true)
|
|
|
- );
|
|
|
- this.lastToast.element.lastElementChild?.addEventListener("click", this.clearToast);
|
|
|
+ if (scoreExplanationHTML != "") {
|
|
|
+ this.lastToast = toast({
|
|
|
+ html: `<span>Ouvrir l'explication du score</span><i class="material-icons">close<i>`,
|
|
|
+ displayLength: Infinity,
|
|
|
+ classes: "action",
|
|
|
+ });
|
|
|
+ this.lastToast.element.firstElementChild?.addEventListener(
|
|
|
+ "click",
|
|
|
+ () => (this.showExplanation = true)
|
|
|
+ );
|
|
|
+ this.lastToast.element.lastElementChild?.addEventListener("click", this.clearToast);
|
|
|
+ }
|
|
|
},
|
|
|
importJsonState(obj: StateJSON, preserve = false) {
|
|
|
// Remove previous content and load the main event title
|
|
|
if (preserve == false) {
|
|
|
+ const prevUuid = this.$store.state.evenement.uuid;
|
|
|
this.$store.commit(MutationTypes.resetState, undefined);
|
|
|
const e = Evenement.fromJSON(obj.evenement);
|
|
|
for (const k of keyofEvent) {
|
|
|
@@ -210,6 +307,9 @@ export default defineComponent({
|
|
|
value: e[k],
|
|
|
});
|
|
|
}
|
|
|
+ if (e.uuid != prevUuid) {
|
|
|
+ this.updatePlanningVersions();
|
|
|
+ }
|
|
|
}
|
|
|
// Import constraint
|
|
|
obj.competences.forEach((c) => {
|