| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- <template>
- <div class="editor-panel">
- <h3>Modifier un créneau</h3>
- <div class="actions">
- <button class="btn small primary" v-on:click="emitCreationOrder">
- <i class="material-icons right">create</i>Nouveau
- </button>
- <button
- class="btn small primary"
- :class="{ disabled: creneau === undefined }"
- v-on:click="emitDuplicateOrder"
- >
- <i class="material-icons right">content_copy</i>Dupliquer
- </button>
- <button
- class="btn small error"
- :class="{ disabled: creneau === null }"
- v-on:click="emitDeleteOrder"
- >
- <i class="material-icons right">delete_forever</i>Supprimer
- </button>
- </div>
- <div class="empty" v-if="creneau === undefined">Veuillez selectioner un creneau.</div>
- <div class="editor-body" v-else>
- <styled-input
- label="Titre"
- id="last_name"
- type="text"
- :modelValue="creneau.title"
- @input="inputListener($event, 'title')"
- />
- <date-picker class="s6" title="Date" id="creneauDate" lang="fr" target="day" v-model="jour" />
- <styled-input
- class="s6"
- label="Heure"
- id="creneauHeure"
- type="text"
- placeholder="hh:mm"
- v-model.lazy="heure"
- />
- <styled-input
- class="s6"
- label="Durée (min)"
- id="creneauDuree"
- type="number"
- v-model.lazy="duree"
- />
- <styled-input
- class="s6"
- label="Heure fin"
- id="disabled"
- type="text"
- :modelValue="endHour"
- disabled
- />
- <styled-input
- class="s6"
- label="Bénévole minimum"
- id="minAttendee"
- type="number"
- :modelValue="creneau.minAttendee"
- :disabled="creneau.isMeal"
- @input="inputListener($event, 'minAttendee')"
- />
- <styled-input
- class="s6"
- label="Bénévole max"
- id="minAttendee"
- type="number"
- :disabled="!creneau.isMeal"
- :modelValue="creneau.maxAttendee"
- @input="inputListener($event, 'maxAttendee')"
- />
- <styled-input
- label="Description"
- id="description"
- type="textarea"
- class="materialize-textarea"
- :modelValue="creneau.description"
- @input="inputListener($event, 'description')"
- >
- </styled-input>
- <chips-input
- label="Compétences & préférences associées"
- id="compétence_selection"
- placeholder="Choisir une condition"
- secondary-placeholder="+ compétence"
- :autocomplete-list="autocompleteCompetencesList"
- :strict-autocomplete="true"
- v-model="competencesStrIdList"
- ></chips-input>
- <chips-input
- label="Bénévoles"
- id="bénevole_selection"
- placeholder="Choisir un bénévole"
- secondary-placeholder="+ bénévole"
- :autocomplete-list="autocompleteBenevolesList"
- :strict-autocomplete="true"
- v-model="benevoleStrIdList"
- ></chips-input>
- <div class="s6 checkbox">
- <checkbox
- label="Créneau repas"
- :modelValue="creneau.isMeal"
- help="Indique au solveur que ce créneau est un créneau repas"
- @input="checkboxListener($event, 'isMeal')"
- />
- <checkbox
- label="Bénévole fixe"
- :modelValue="creneau.fixedAttendee"
- help="Indique au solveur de ne pas changer l'affectation de ce créneau"
- @input="checkboxListener($event, 'fixedAttendee')"
- />
- </div>
- <styled-input
- label="Pénibilité"
- id="penibility"
- type="number"
- class="s6"
- :modelValue="creneau.penibility"
- @input="inputListener($event, 'penibility')"
- style="display: none"
- />
- </div>
- </div>
- </template>
- <script lang="ts">
- import { defineComponent, PropType } from "vue";
- import Creneau from "@/models/Creneau";
- import AutocompleteOptions from "@/models/AutocompleteOptions";
- import styledInput from "./input.vue";
- import chipsInput from "@/components/SelectChipInput.vue";
- import DatePicker from "@/components/date-picker.vue";
- import checkbox from "./checkBox.vue";
- import dayjs from "dayjs";
- import "@/assets/css/editor-panel.css";
- import { MutationTypes } from "@/store/Mutations";
- import Benevole from "@/models/Benevole";
- import Competence from "@/models/Competence";
- // TODO Understand why the component only syncro after a first edition of the date
- export default defineComponent({
- name: "EditeurCreneau",
- components: { chipsInput, styledInput, checkbox, DatePicker },
- props: {
- creneau: {
- type: Object as PropType<Creneau>,
- },
- },
- data: function () {
- return {
- jour: "",
- heure: "",
- duree: "",
- benevoleStrIdList: [] as Array<string>,
- competencesStrIdList: [] as Array<string>,
- };
- },
- watch: {
- "creneau.start": function () {
- this.updateForm();
- },
- "creneau.end": function () {
- this.updateForm();
- },
- "creneau.benevoleIdList": function (val: Array<number>) {
- this.benevoleStrIdList = val.map((s) => s.toString());
- },
- creneau: function (val: Creneau) {
- this.competencesStrIdList = val.competencesIdList.map((s) => s.toString());
- },
- competencesStrIdList(val: Array<string>) {
- if (this.creneau) {
- const new_arr = val.map((s) => parseInt(s));
- const old_arr = this.creneau?.competencesIdList;
- if (
- new_arr.length !== old_arr?.length ||
- new_arr.reduce((acc: boolean, n: number) => acc && old_arr.includes(n), true)
- ) {
- this.updateCreneau("competencesIdList", new_arr);
- }
- }
- },
- benevoleStrIdList(new_val: Array<string>) {
- if (this.creneau) {
- const new_list = new_val.map((s) => parseInt(s));
- const old_list = this.creneau?.benevoleIdList;
- const addList = new_list.filter((i) => !old_list.includes(i));
- const removeList = old_list.filter((i) => !new_list.includes(i));
- addList.forEach(this.addBenevole2Creneau);
- removeList.forEach(this.removeBenevole2Creneau);
- }
- },
- jour: function () {
- this.updateDates();
- },
- heure: function () {
- this.updateDates();
- },
- duree: function () {
- this.updateDates();
- },
- },
- computed: {
- duration(): number {
- return parseFloat(this.duree) ?? 0;
- },
- concatStart(): string {
- return (
- this.jour +
- " " +
- this.heure
- .split(":")
- .map((c) => ("0" + c).slice(-2))
- .join(":")
- );
- },
- validStart(): boolean {
- return /\d{4}\/\d{1,2}\/\d{1,2}/.test(this.jour) && dayjs(this.concatStart).isValid();
- },
- startDate(): Date {
- return dayjs(this.concatStart, "YYYY/MM/DD HH:mm").toDate();
- },
- endDate(): Date {
- return dayjs(this.startDate).add(this.duration, "m").toDate();
- },
- endHour(): string {
- return this.endDate.toTimeString().substring(0, 5);
- },
- validDuree(): boolean {
- return !isNaN(parseFloat(this.duree));
- },
- autocompleteBenevolesList(): Array<AutocompleteOptions> {
- const precursor = this.$store.state.benevoleList
- .map((benevole) => {
- return {
- id: benevole.id + "",
- name: benevole.fullname,
- score: this.getbenevoleScore(benevole),
- collide: this.getBenevoleCollision(benevole),
- };
- })
- .sort((a, b) => {
- 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 = o.collide ? "unavailable" : "";
- if (o.score == 3) {
- classes = "error";
- }
- if (o.score == 2) {
- classes = "warning";
- }
- if (o.score == 1) {
- classes = "warning";
- }
- return {
- id: o.id,
- name: o.name,
- class: classes,
- };
- });
- return output;
- },
- competenceList(): Array<Competence> {
- const output = [];
- for (const idStr of this.competencesStrIdList) {
- const id = parseInt(idStr);
- if (id) {
- const c = this.$store.getters.getCompetenceById(id);
- if (c) output.push(c);
- }
- }
- return output;
- },
- autocompleteCompetencesList(): Array<AutocompleteOptions> {
- return this.$store.state.competenceList.map((competence) => {
- return { id: competence.id + "", name: competence.fullname };
- });
- },
- },
- methods: {
- getbenevoleScore(benevole: Benevole): number {
- const scoreList = this.competenceList.map((c) =>
- benevole.competenceIdList.includes(c.id) ? 0 : c.score
- );
- 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)
- this.updateCreneau("start", this.startDate);
- if (Math.abs(this.endDate.getTime() - this.creneau.end.getTime()) > 1000)
- this.updateCreneau("end", this.endDate);
- }
- },
- updateCreneau<K extends keyof Creneau>(field: K, value: Creneau[K]) {
- if (this.creneau) {
- const payload = {
- id: this.creneau.id,
- field: field,
- value: value,
- };
- this.$emit("edit", payload);
- }
- },
- addBenevole2Creneau(id: number) {
- if (this.creneau)
- this.$store.commit(MutationTypes.addBenevole2Creneau, {
- creneauId: this.creneau.id,
- benevoleId: id,
- });
- },
- removeBenevole2Creneau(id: number) {
- if (this.creneau)
- this.$store.commit(MutationTypes.removeBenevole2Creneau, {
- creneauId: this.creneau.id,
- benevoleId: id,
- });
- },
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- inputListener(event: any, field: keyof Creneau) {
- this.updateCreneau(field, event.target.value);
- },
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- checkboxListener(event: any, field: keyof Creneau) {
- this.updateCreneau(field, event.target.checked);
- },
- updateForm: function () {
- if (this.creneau) {
- const startDate = dayjs(this.creneau.start);
- this.jour = startDate.format("YYYY/MM/DD");
- this.heure = startDate.format("HH:mm");
- this.duree = Math.round(
- (this.creneau.end.getTime() - this.creneau.start.getTime()) / 1000 / 60
- ).toString();
- }
- },
- emitDuplicateOrder: function () {
- this.$emit("duplicate", this.creneau);
- },
- emitCreationOrder: function () {
- this.$emit("create");
- },
- emitDeleteOrder: function () {
- this.$emit("delete", this.creneau);
- },
- },
- mounted() {
- this.updateForm();
- if (this.creneau) {
- this.benevoleStrIdList = this.creneau.benevoleIdList.map((s) => s.toString());
- this.competencesStrIdList = this.creneau.competencesIdList.map((s) => s.toString());
- }
- },
- });
- </script>
- <style scoped>
- .checkbox {
- padding: 4px;
- }
- </style>
|