Browse Source

implment change from marine feedback

tripeur 4 years ago
parent
commit
1f6a387936

+ 14 - 14
package-lock.json

@@ -122,6 +122,11 @@
         }
       }
     },
+    "@juggle/resize-observer": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz",
+      "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw=="
+    },
     "@mrmlnc/readdir-enhanced": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
@@ -3327,9 +3332,9 @@
       }
     },
     "core-js": {
-      "version": "3.13.1",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.13.1.tgz",
-      "integrity": "sha512-JqveUc4igkqwStL2RTRn/EPFGBOfEZHxJl/8ej1mXJR75V3go2mFF4bmUYkEIT1rveHKnkUlcJX/c+f1TyIovQ=="
+      "version": "3.15.1",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.1.tgz",
+      "integrity": "sha512-h8VbZYnc9pDzueiS2610IULDkpFFPunHwIpl8yRwFahAEEdSpHlTy3h3z3rKq5h11CaUdBEeRViu9AYvbxiMeg=="
     },
     "core-util-is": {
       "version": "1.0.2",
@@ -6908,7 +6913,7 @@
       "dev": true
     },
     "jc-timeline": {
-      "version": "git+http://gitlab.jaquin.fr/clovis/jc-timeline.git#591b6ac99a0208ef0ee44ac805937599876eb826",
+      "version": "git+http://gitlab.jaquin.fr/clovis/jc-timeline.git#700acd245a76a3ff67529e6ea312dcb4ed4a84d9",
       "from": "git+http://gitlab.jaquin.fr/clovis/jc-timeline.git",
       "requires": {
         "dayjs": "^1.10.4",
@@ -10632,11 +10637,6 @@
       "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
       "dev": true
     },
-    "resize-observer-polyfill": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
-      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
-    },
     "resolve": {
       "version": "1.20.0",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
@@ -11233,16 +11233,16 @@
       }
     },
     "simplebar": {
-      "version": "5.3.3",
-      "resolved": "https://registry.npmjs.org/simplebar/-/simplebar-5.3.3.tgz",
-      "integrity": "sha512-OfuSX47Axq9aR6rp9WK3YefAg+1Qw3UKKxS46PdElPpd+FWXMj17/nispYxsHtU3F7mv+ilmqELWmRt7KUgHgg==",
+      "version": "5.3.4",
+      "resolved": "https://registry.npmjs.org/simplebar/-/simplebar-5.3.4.tgz",
+      "integrity": "sha512-2mCaVdiroCKmXuD+Qfy+QSE32m5BMuZ4ssHvRD1QEPYH95Re/kox7j/Wy0Hje8Uo7LY7O6JK3XSNJmesGlsP8Q==",
       "requires": {
+        "@juggle/resize-observer": "^3.3.1",
         "can-use-dom": "^0.1.0",
         "core-js": "^3.0.1",
         "lodash.debounce": "^4.0.8",
         "lodash.memoize": "^4.1.2",
-        "lodash.throttle": "^4.1.1",
-        "resize-observer-polyfill": "^1.5.1"
+        "lodash.throttle": "^4.1.1"
       }
     },
     "sirv": {

+ 9 - 4
src/PlannerApp.vue

@@ -3,7 +3,7 @@
   <div class="main-container">
     <router-view
       @export="exportStateToJson"
-      @import="(e) => importJsonState(e, false)"
+      @import="importState"
       @localSave="localSave"
       @save="save"
       @solve="solve"
@@ -12,7 +12,7 @@
     <div v-if="optimisationInProgress" class="modal">
       <div class="modal-content">
         <h3>Optimisation en cours</h3>
-        <p>L'ordinateur calcul un meilleur planning</p>
+        <p>L'ordinateur calcule un meilleur planning</p>
         <div class="spinner"><span class="material-icons"> sync </span></div>
       </div>
     </div>
@@ -20,7 +20,7 @@
       <div class="modal-content" v-html="explanation" @click="(e) => e.stopPropagation()"></div>
     </div>
   </div>
-  <c-footer />
+  <c-footer v-if="showFooter" />
 </template>
 <script lang="ts">
 import { defineComponent } from "vue";
@@ -50,7 +50,7 @@ const tabs: Array<HeaderLink> = [
   { to: "/planner", name: "Accueil" },
   { to: "/planner/evenement", name: "Evenement" },
   { to: "/planner/planning", name: "Planning" },
-  { to: "/planner/competences", name: "Gestion des compétences" },
+  { to: "/planner/contraintes", name: "Gestion des contraintes" },
   { to: "/planner/benevoles", name: "Gestion des bénévoles" },
   { to: "/planner/planningIndividuel", name: "Planning Individuel" },
 ];
@@ -66,6 +66,11 @@ export default defineComponent({
       tabs,
     };
   },
+  computed: {
+    showFooter(): boolean {
+      return this.$route.name != "Planning";
+    },
+  },
   mounted() {
     const url = `${API_URL}api/evenements`;
     fetch(url)

+ 8 - 0
src/assets/css/editor-panel.css

@@ -4,6 +4,14 @@
   box-shadow: 0 0 2px 2px var(--color-neutral-600);
   height: 100%;
 }
+.editor-panel.planning {
+  height: auto;
+  z-index: 10;
+  position: fixed;
+  background: white;
+  right: 2rem;
+  top: 4.5rem;
+}
 .editor-panel > .actions {
   display: flex;
   justify-content: center;

+ 3 - 0
src/assets/css/multiple-select.css

@@ -83,6 +83,9 @@
 .dropdown-options.pickable > div.error {
   background-color: #f9ccd4;
 }
+.dropdown-options.pickable > div.unavailable {
+  text-decoration: line-through;
+}
 .select-multiple > .select-multiple-value {
   all: initial;
   display: block;

+ 1 - 1
src/components/CreneauViewer.vue

@@ -44,7 +44,7 @@
           </div>
         </template>
         <template v-if="nextCreneau">
-          <div class="agenda-creneau-details--title">Ceux qui te remplacerons</div>
+          <div class="agenda-creneau-details--title">Les personnes qui arrivent après toi</div>
           <div class="agenda-creneau-details--content">
             <div
               class="chip chip--clickable"

+ 26 - 6
src/components/EditeurCreneau.vue

@@ -243,20 +243,27 @@ export default defineComponent({
     autocompleteBenevolesList(): Array<AutocompleteOptions> {
       const precursor = this.$store.state.benevoleList
         .map((benevole) => {
-          const score = this.getbenevoleScore(benevole);
-
           return {
             id: benevole.id + "",
             name: benevole.fullname,
-            score,
+            score: this.getbenevoleScore(benevole),
+            collide: this.getBenevoleCollision(benevole),
           };
         })
         .sort((a, b) => {
-          const s = a.score - b.score;
-          return s == 0 ? a.name.localeCompare(b.name) : s;
+          let s = a.score - b.score;
+          if (s != 0) {
+            return s;
+          }
+          // put the available volunteer first
+          s = (a.collide ? 1 : 0) - (b.collide ? 1 : 0);
+          if (s != 0) {
+            return s;
+          }
+          return a.name.localeCompare(b.name);
         });
       const output = precursor.map((o) => {
-        let classes = "";
+        let classes = o.collide ? "unavailable" : "";
         if (o.score == 3) {
           classes = "error";
         }
@@ -298,6 +305,19 @@ export default defineComponent({
       );
       return scoreList.length == 0 ? 0 : Math.max(...scoreList);
     },
+    getBenevoleCollision(benevole: Benevole) {
+      if (this.creneau != undefined) {
+        const current = this.creneau;
+        const concurence = benevole.creneauIdList
+          .filter((id) => id != this.creneau?.id)
+          .map((id) => this.$store.getters.getCreneauById(id))
+          .filter((o) => o != undefined) as Array<Creneau>;
+
+        return concurence.map((c) => c.collide(current)).some((b) => b);
+      } else {
+        return false;
+      }
+    },
     updateDates: function (): void {
       if (this.creneau && this.validDuree && this.validStart) {
         if (Math.abs(this.startDate.getTime() - this.creneau.start.getTime()) > 1000)

+ 3 - 0
src/components/SelectChipInput.vue

@@ -101,6 +101,9 @@ export default defineComponent({
     modelValue: function () {
       this.updateItems();
     },
+    autocompleteList: function () {
+      this.updateItems();
+    },
   },
   emit: ["update:modelValue"],
   methods: {

+ 6 - 1
src/models/Creneau.ts

@@ -1,5 +1,6 @@
 import dayjs from "dayjs";
 import { Event, EventJSON } from "jc-timeline";
+import Benevole from "./Benevole";
 import { MealSlot, TimeslotRaw } from "./SolverInput";
 
 export interface ICreneau {
@@ -123,9 +124,13 @@ class Creneau implements ICreneau {
   }
   updateEventContent(): void {
     const missingbenevole = Math.max(this.minAttendee - this.benevoleIdList.length, 0);
-    const spanClass = this.benevoleIdList.length == 0 ? "red" : missingbenevole > 0 ? "orange" : "";
+    const spanClass =
+      this.benevoleIdList.length == 0 ? "red" : missingbenevole != 0 ? "orange" : "";
     this.event.content = `<div class="bubble ${spanClass}">${this.benevoleIdList.length} / ${this.minAttendee}</div>`;
   }
+  collide(other: Creneau): boolean {
+    return this.event.start < other.event.end && other.event.start < this.event.end;
+  }
   toJSON(): CreneauJSON {
     const e = this.event.toJSON();
     delete e.content;

+ 2 - 2
src/router/main.ts

@@ -24,8 +24,8 @@ const routes: Array<RouteRecordRaw> = [
     component: Planning,
   },
   {
-    path: "/planner/competences",
-    name: "Competence",
+    path: "/planner/contraintes",
+    name: "Contraintes",
     component: CompetenceManager,
   },
   {

+ 14 - 3
src/store/Mutations.ts

@@ -19,6 +19,7 @@ export enum MutationTypes {
   clearBenevole2Creneau = "clearBenevole2Creneau",
 
   addCreneauGroup = "addCreneauGroup",
+  addCreneauGroupAt = "addCreneauGroupAt",
   removeCreneauGroup = "removeCreneauGroup",
   editCreneauGroup = "editCreneauGroup",
   reorderCreneauGroup = "reorderCreneauGroup",
@@ -57,6 +58,7 @@ export type Mutations<S = State> = {
   ): boolean;
 
   [MutationTypes.addCreneauGroup](state: S, payload: Ressource): void;
+  [MutationTypes.addCreneauGroupAt](state: S, payload: { r: Ressource; pos: number }): void;
   [MutationTypes.removeCreneauGroup](state: S, payload: Ressource): void;
   [MutationTypes.editCreneauGroup]<K extends keyof Ressource>(
     state: S,
@@ -102,7 +104,7 @@ export const mutations: MutationTree<State> & Mutations = {
 
   // Creneau Management
   [MutationTypes.addCreneau](state, creneau) {
-    state.creneauList = [...state.creneauList, creneau];
+    state.creneauList = [creneau, ...state.creneauList];
   },
   [MutationTypes.removeCreneau](state, creneau) {
     state.creneauList = state.creneauList.filter((c) => c.id !== creneau.id);
@@ -144,6 +146,15 @@ export const mutations: MutationTree<State> & Mutations = {
   [MutationTypes.addCreneauGroup](state, payload) {
     state.creneauGroupList = [...state.creneauGroupList, payload];
   },
+  [MutationTypes.addCreneauGroupAt](state, payload) {
+    if (payload.pos < 1) {
+      state.creneauGroupList = [payload.r, ...state.creneauGroupList];
+    } else if (payload.pos > state.creneauGroupList.length) {
+      state.creneauGroupList = [...state.creneauGroupList, payload.r];
+    } else {
+      state.creneauGroupList.splice(payload.pos, 0, payload.r);
+    }
+  },
   [MutationTypes.removeCreneauGroup](state, payload) {
     state.creneauGroupList = state.creneauGroupList.filter((c) => c.id !== payload.id);
   },
@@ -157,7 +168,7 @@ export const mutations: MutationTree<State> & Mutations = {
   },
   // Benevole Management
   [MutationTypes.addBenevole](state, payload) {
-    state.benevoleList = [...state.benevoleList, payload];
+    state.benevoleList = [payload, ...state.benevoleList];
   },
   [MutationTypes.removeBenevole](state, payload) {
     state.creneauList.forEach(
@@ -176,7 +187,7 @@ export const mutations: MutationTree<State> & Mutations = {
   },
   // Constraint Management
   [MutationTypes.addConstraint](state, payload) {
-    state.competenceList = [...state.competenceList, payload];
+    state.competenceList = [payload, ...state.competenceList];
   },
   [MutationTypes.removeConstraint](state, payload) {
     const filterFunction = (id: number) => id !== payload.id;

+ 1 - 0
src/views/CompetenceManager.vue

@@ -95,6 +95,7 @@ export default defineComponent({
     createCompetence() {
       const comp = Competence.fromObject({ name: "Nouvelle contrainte", description: "" });
       this.$store.commit(MutationTypes.addConstraint, comp);
+      this.currentCompetence = comp;
     },
     deleteCompetence(payload: Competence) {
       this.$store.commit(MutationTypes.removeConstraint, payload);

+ 13 - 9
src/views/Planning.vue

@@ -35,6 +35,7 @@
   </div>
 
   <editeur-creneau
+    class="planning"
     v-if="currentCreneau"
     :creneau="currentCreneau"
     @create="createCreneau"
@@ -43,6 +44,7 @@
     @edit="updateCreneau"
   ></editeur-creneau>
   <editeur-ligne
+    class="planning"
     v-else
     :creneauGroupId="currentCreneauGroupId"
     @create="createRessource"
@@ -91,10 +93,15 @@ export default defineComponent({
   },
   methods: {
     createCreneau(): void {
+      // take the current crenau as reference if any
       const groupId = this.currentCreneau
         ? this.currentCreneau.ressourceId
-        : this.currentCreneauGroup
+        : // else the current row as reference if any
+        this.currentCreneauGroup
         ? this.currentCreneauGroup.id
+        : // else the first row as reference if any
+        this.creneauList.length > 0
+        ? this.creneauList[0].id
         : false;
       if (groupId) {
         const start = new Date(this.timeline.start);
@@ -139,7 +146,7 @@ export default defineComponent({
         id: uuidv4(),
         title: "Nouvelle ligne",
       });
-      this.$store.commit(MutationTypes.addCreneauGroup, ressource);
+      this.$store.commit(MutationTypes.addCreneauGroupAt, { pos: 0, r: ressource });
       this.timeline.clearSelectedItems();
       this.timeline.addRessource(ressource).selected = true;
       this.currentCreneauGroup = ressource;
@@ -335,16 +342,13 @@ export default defineComponent({
 }
 .timeline {
   margin: 0px 16px;
-  width: calc(100% - 400px);
-}
-@media (min-width: 600px) {
-  .timeline {
-    width: calc(95% - 400px);
-  }
+  width: 100%;
 }
 jc-timeline {
   margin-top: 8px;
-  display: block;
+  display: flex;
+  flex-direction: column;
+  height: calc(100vh - 13rem);
 }
 .actions {
   display: inline-flex;