Browse Source

first commit

tripeur 4 years ago
parent
commit
d0075ef889

+ 123 - 0
src/components/PlanningViewRenderer.vue

@@ -0,0 +1,123 @@
+<template>
+  <h1>Planning {{ planningView.name }}</h1>
+  <jc-timeline
+    ref="timeline"
+    :slotduration="slotduration"
+    :legendspan="legendspan"
+    :slotwidth="slotwidth"
+    :start="start.toDate().toISOString()"
+  />
+</template>
+
+<script lang="ts">
+import { defineComponent, PropType } from "vue";
+import { v4 as uuidv4 } from "uuid";
+import { PlanningView } from "@/models/PlanningView";
+import Creneau from "@/models/Creneau";
+
+import Timeline from "jc-timeline/lib/Timeline";
+import { Ressource } from "jc-timeline/lib/Ressource";
+import { Event as jcEvent } from "jc-timeline/lib/Event";
+
+import { pickColor2 } from "@/utils/colorPicker";
+import dayjs from "dayjs";
+
+export default defineComponent({
+  data() {
+    return {
+      start: dayjs().startOf("hour"),
+      end: dayjs().add(1, "days").subtract(1, "h").endOf("h"),
+      slotduration: 30,
+      legendspan: 2,
+      slotwidth: 50,
+    };
+  },
+  props: {
+    planningView: { type: Object as PropType<PlanningView> },
+  },
+  watch: {
+    eventList() {
+      this.updateTimeline();
+    },
+    planningView(val: PlanningView) {
+      this.start = dayjs(val.start);
+      this.end = dayjs(val.end);
+      this.timeline.end = this.end.subtract(1, "h").endOf("h").toISOString();
+      this.updateTimeline();
+    },
+    "planningView.start": function (val: Date) {
+      this.start = dayjs(val);
+      this.updateTimeline();
+    },
+    "planningView.end": function (val: Date) {
+      this.end = dayjs(val);
+      this.timeline.end = this.end.subtract(1, "h").endOf("h").toISOString();
+      this.updateTimeline();
+    },
+  },
+  computed: {
+    timeline(): Timeline {
+      const output = this.$refs["timeline"];
+      return output as Timeline;
+    },
+    filteredCreneau(): Array<Creneau> {
+      const ids = new Set(this.filteredRessource.map((o) => o.id));
+      const s = this.start.toDate();
+      const e = this.end.toDate();
+      return this.$store.state.creneauList
+        .filter((c) => ids.has(c.ressourceId))
+        .filter((c) => (s <= c.start && c.start <= e) || (s <= c.end && c.end <= e));
+    },
+    filteredRessource(): Array<Ressource> {
+      const ids = new Set(this.planningView?.ressourceUuidList);
+      const parents = this.$store.state.creneauList.filter((r) => ids.has(r.id));
+      return this.$store.state.creneauGroupList.filter(
+        (r) => ids.has(r.parentId) || parents.some((p) => r.descendantOf(p))
+      );
+    },
+    eventList(): Array<jcEvent> {
+      return this.filteredCreneau.flatMap((c) =>
+        c.benevoleIdList.map((id) => {
+          const b = this.$store.getters.getBenevoleById(id);
+          return jcEvent.fromJSON({
+            ressourceId: c.ressourceId,
+            start: c.start.toISOString(),
+            end: c.end.toISOString(),
+            title: b?.shortame,
+            editable: false,
+            ressourceEditable: false,
+            id: uuidv4(),
+            bgColor: pickColor2(b?.shortame ?? ""),
+          });
+        })
+      );
+    },
+  },
+  methods: {
+    updateTimeline(): void {
+      this.$nextTick(() => {
+        this.timeline
+          .getRessources()
+          .map((c) => c.id)
+          .forEach((id) => this.timeline.removeRessourceById(id));
+        this.timeline.addRessources(this.filteredRessource);
+        this.timeline.addEvents(this.eventList);
+        this.timeline.requestUpdate();
+      });
+    },
+  },
+  mounted() {
+    this.timeline.setLegendUnitFormat("d", "dddd D MMMM");
+    this.$nextTick(() => {
+      this.start = dayjs(this.$store.state.evenement.startingDate);
+      this.end = this.start.add(1, "d");
+    });
+  },
+});
+</script>
+
+<style scoped>
+h1 {
+  margin-top: 0;
+}
+</style>

+ 8 - 0
src/models/PlanningView.ts

@@ -0,0 +1,8 @@
+export type PlanningView = {
+  uuid: string;
+  planningUuid: string;
+  name: string;
+  start: Date;
+  end: Date;
+  ressourceUuidList: Array<string>;
+};

+ 16 - 0
src/utils/colorPicker.ts

@@ -0,0 +1,16 @@
+const hashCode = function (str: string): number {
+  let hash = 0;
+  for (let i = 0; i < str.length; i++) {
+    hash = str.charCodeAt(i) + ((hash << 5) - hash);
+  }
+  return hash;
+};
+
+export function pickColor(str: string): string {
+  return "#" + ("000000" + (hashCode(str) & 0x00ffffff).toString(16)).slice(-6);
+}
+export function pickColor2(str: string): string {
+  let hue = hashCode(str) % 360;
+  if (hue < 0) hue += 360;
+  return `hsl(${hue}, 75%, 40%)`;
+}

+ 168 - 0
src/views/PlanningViewManager.vue

@@ -0,0 +1,168 @@
+<template>
+  <div class="planning-view-container">
+    <div class="planning-view-panel">
+      <div
+        class="item"
+        :class="{ selected: view.uuid == currentPlanningView.uuid }"
+        v-for="view in planningViewList"
+        :key="view.uuid"
+        @click="loadView(view.uuid)"
+      >
+        {{ view.name }}
+        <i class="material-icons inline button" @click="showEdit = true">edit</i>
+      </div>
+      <div class="item" @click="newView">
+        <i class="material-icons inline">add</i> Ajouter une vue
+      </div>
+    </div>
+    <div class="planning-view-timeline" v-if="currentPlanningView != null">
+      <planning-view-renderer :planningView="currentPlanningView" />
+    </div>
+    <div v-else>Veuillez créer ou charge une vue</div>
+  </div>
+  <div v-if="showEdit && currentPlanningView != null" class="modal" @click="closeEdit">
+    <div class="modal-content" ref="edit">
+      <styled-input label="Titre" id="planning-view-title" v-model="currentPlanningView.name" />
+      <date-picker
+        label="Début"
+        id="planning-view-start"
+        lang="fr"
+        target="hour"
+        v-model="currentPlanningView.start"
+      />
+      <date-picker
+        label="Fin"
+        id="planning-view-end"
+        lang="fr"
+        target="hour"
+        v-model="currentPlanningView.end"
+      />
+      <select-chip-input
+        title="Ligne"
+        placeholder="Selectionner les lignes à afficher dans ce planning"
+        secondaryPlaceholder="Rajouter une ligne"
+        v-model="currentPlanningView.ressourceUuidList"
+        :autocompleteList="autocompleteList"
+      />
+      <button class="btn small error" v-on:click="deleteVue">
+        <i class="material-icons">delete_forever</i>Supprimer cette vue
+      </button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { PlanningView } from "@/models/PlanningView";
+import DatePicker from "@/components/date-picker.vue";
+import styledInput from "@/components/input.vue";
+import SelectChipInput from "@/components/SelectChipInput.vue";
+
+import PlanningViewRenderer from "@/components/PlanningViewRenderer.vue";
+import { v4 as uuidv4 } from "uuid";
+import { defineComponent } from "vue";
+import AutocompleteValues from "@/models/AutocompleteOptions";
+
+const API_URL = process.env.VUE_APP_API_URL;
+
+export default defineComponent({
+  components: { PlanningViewRenderer, DatePicker, styledInput, SelectChipInput },
+  data() {
+    return {
+      showEdit: false,
+      currentPlanningView: null as null | PlanningView,
+      planningViewList: [] as Array<PlanningView>,
+    };
+  },
+  computed: {
+    autocompleteList(): Array<AutocompleteValues> {
+      return this.$store.state.creneauGroupList
+        .filter((r) => r.parentId == "")
+        .map((o) => {
+          return { id: o.id.toString(), name: o.title };
+        });
+    },
+  },
+  methods: {
+    closeEdit(e: MouseEvent) {
+      if (!(this.$refs.edit as HTMLElement).contains(e.target as Node)) this.showEdit = false;
+    },
+    newView() {
+      const evt = this.$store.state.evenement;
+      const planning: PlanningView = {
+        uuid: uuidv4(),
+        planningUuid: evt.uuid,
+        name: "Nouvelle vue",
+        start: evt.start,
+        end: evt.end,
+        ressourceUuidList: [],
+      };
+      this.currentPlanningView = planning;
+      this.planningViewList.push(planning);
+    },
+    deleteVue() {
+      if (this.currentPlanningView && confirm("Êtes vous sûr de vouloir supprimer cette vue?")) {
+        const uuid = this.currentPlanningView.uuid;
+        this.planningViewList = this.planningViewList.filter((p) => p.uuid != uuid);
+        this.currentPlanningView = null;
+        this.showEdit = false;
+      }
+    },
+    loadView(uuid: string) {
+      this.currentPlanningView = this.planningViewList.find((p) => uuid == p.uuid) ?? null;
+    },
+  },
+});
+</script>
+
+<style scoped>
+.planning-view-container {
+  display: flex;
+  flex-direction: row;
+  width: 100%;
+}
+.planning-view-panel {
+  display: block;
+  width: 200px;
+  margin: -8px 0 0 -8px;
+}
+.planning-view-panel > .item {
+  padding: 16px;
+  white-space: nowrap;
+  cursor: pointer;
+}
+.planning-view-panel > .item.selected {
+  background: var(--color-accent-600);
+  color: var(--color-neutral-100);
+}
+.planning-view-panel > .item:hover {
+  background: var(--color-accent-900);
+}
+.planning-view-timeline {
+  padding: 0px 8px;
+  width: calc(100% - 200px);
+}
+@media print {
+  .planning-view-panel {
+    display: none;
+  }
+  .planning-view-timeline {
+    padding: 0;
+    width: 100%;
+  }
+}
+.material-icons.inline {
+  vertical-align: bottom;
+  font-size: 20px;
+}
+.material-icons.button {
+  position: relative;
+  padding: 0px 4px;
+}
+.material-icons.button:hover {
+  position: relative;
+  padding: 4px;
+  margin: -4px 0px;
+  border-radius: 4px;
+  background: var(--color-accent-800);
+}
+</style>

+ 1 - 18
src/views/PrintablePlanning.vue

@@ -19,30 +19,14 @@ import { defineComponent } from "vue";
 import Timeline from "jc-timeline/lib/Timeline";
 import DatePicker from "@/components/date-picker.vue";
 
-import FilterPanel, { FilterParam } from "@/components/filteringPanel.vue";
 import { Event as jcEvent } from "jc-timeline/lib/Event";
 import dayjs from "dayjs";
 import Creneau from "@/models/Creneau";
 import { Ressource } from "jc-timeline";
 import { v4 as uuidv4 } from "uuid";
 
-function hashCode(str: string) {
-  let hash = 5381;
-  for (var i = 0; i < str.length; i++) {
-    hash = str.charCodeAt(i) + ((hash << 5) - hash);
-  }
-  return hash;
-}
+import { pickColor2 } from "@/utils/colorPicker";
 
-function pickColor2(str: string) {
-  let hue = hashCode(str) % 360;
-  if (hue < 0) hue += 360;
-  return `hsl(${hue}, 75%, 40%)`;
-}
-function pickColor(str: string) {
-  return "#" + ("000000" + (hashCode(str) & 0x00ffffff).toString(16)).slice(-6);
-}
-const filters: Array<FilterParam> = [];
 export default defineComponent({
   name: "PrintablePlanning",
   components: { DatePicker },
@@ -53,7 +37,6 @@ export default defineComponent({
       slotduration: 30,
       legendspan: 2,
       slotwidth: 50,
-      filters,
     };
   },
   watch: {