github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/server/frontend/bv/src/pages/vote/personne/App.vue (about) 1 <template> 2 <base-app :title="title"> 3 <success v-if="output" :output="output"></success> 4 <error v-if="output" :output="output"></error> 5 6 <b-container class="my-2 mb-3 mx-auto"> 7 <b-card body-class="py-3 px-3"> 8 <b-card-title> 9 <b-row> 10 <b-col> 11 Votes en cours 12 <b-badge variant="fushia" v-if="votesCourants > 0"> 13 {{ votesCourants }} 14 </b-badge> 15 </b-col> 16 <b-col class="text-right"> 17 <b-button 18 :href="urlExportVotes" 19 target="_blank" 20 variant="info" 21 title="Télécharger au format PDF" 22 > 23 <b-icon-download></b-icon-download> 24 Capture 25 </b-button> 26 <b-button 27 @click="refresh" 28 variant="light" 29 class="ml-2" 30 v-b-tooltip 31 title="Rafraichir les données" 32 > 33 <b-icon-arrow-repeat></b-icon-arrow-repeat> 34 </b-button> 35 </b-col> 36 </b-row> 37 </b-card-title> 38 <b-overlay :show="loading"> 39 <b-card-text> 40 <b-card v-for="(vote, i) in votes" :key="i" class="my-2"> 41 <b-card-title> 42 {{ vote.nom }} 43 <b-badge variant="warning" v-if="vote.is_locked" 44 >Vote cloturé</b-badge 45 > 46 </b-card-title> 47 <b-card-sub-title class="mb-2"> 48 {{ vote.description }}</b-card-sub-title 49 > 50 <b-card-text> 51 <div class="border rounded border-secondary px-2 pt-2"> 52 <b-form-group> 53 <template v-slot:label> 54 Mon vote 55 <b-badge v-if="hasVoted(vote)"> 56 dernier vote : {{ formatDateTime(vote.time) }} 57 </b-badge> 58 </template> 59 <b-form-checkbox-group 60 v-if="vote.is_qcm" 61 :checked="vote.choix || []" 62 @change="onCheckboxInputVote(vote, $event)" 63 :options="getItems(vote)" 64 :disabled="vote.is_locked" 65 > 66 </b-form-checkbox-group> 67 <b-form-radio-group 68 v-else 69 :options="getItems(vote)" 70 :checked="radioChecked(vote.choix)" 71 @change="onRadioInputVote(vote, $event)" 72 :disabled="vote.is_locked" 73 > 74 </b-form-radio-group> 75 </b-form-group> 76 </div> 77 </b-card-text> 78 <b-row> 79 <b-col> 80 <b-button 81 :variant="isVoteDeletable(vote) ? 'danger' : 'light'" 82 :disabled="!isVoteDeletable(vote)" 83 @click="resetVote(vote)" 84 >Effacer mon vote</b-button 85 > 86 </b-col> 87 <b-col class="text-right"> 88 <b-button 89 :variant="isVoteDoable(vote) ? 'success' : 'light'" 90 @click="save(vote)" 91 :disabled="!isVoteDoable(vote)" 92 >Enregistrer mon vote</b-button 93 > 94 </b-col> 95 </b-row> 96 </b-card> 97 </b-card-text> 98 </b-overlay> 99 </b-card> 100 </b-container> 101 </base-app> 102 </template> 103 104 <script lang="ts"> 105 import Vue from "vue"; 106 import Component from "vue-class-component"; 107 import BaseApp from "@/BaseApp.vue"; 108 import Success from "@/shared/notifications/Success.vue"; 109 import Error from "@/shared/notifications/Error.vue"; 110 import { C, BASE_URL } from "./controller"; 111 import { VotePersonneComplet, Ids, MetaPersonne } from "@/shared/logic/types"; 112 import { 113 formatDateTime, 114 decodeServerPayload, 115 isNullDateString 116 } from "../../../shared/logic/utils"; 117 118 type DirtyVote = VotePersonneComplet & { isDirty?: boolean }; 119 120 @Component({ 121 components: { BaseApp, Success, Error } 122 }) 123 export default class App extends Vue { 124 personne = decodeServerPayload<MetaPersonne>(); 125 126 output = C.notifications; 127 loading = false; 128 votes: DirtyVote[] = []; 129 130 formatDateTime = formatDateTime; 131 132 urlExportVotes = BASE_URL + "/export"; 133 134 created() { 135 this.refresh(); 136 } 137 138 async refresh() { 139 this.loading = true; 140 const data = await C.getVotes(); 141 this.loading = false; 142 if (data == undefined) return; 143 this.votes = data || []; 144 C.notifications.success = { 145 title: "Propositions", 146 message: "Propositions chargées" 147 }; 148 } 149 150 get title() { 151 if (this.personne == null) return ""; 152 return "Page de vote de " + this.personne.prenom_nom; 153 } 154 155 get votesCourants() { 156 return this.votes.filter(v => isNullDateString(v.time)).length; 157 } 158 159 getItems(vote: VotePersonneComplet) { 160 return (vote.candidats || []).map(cd => { 161 return { value: cd.id, text: cd.label }; 162 }); 163 } 164 165 hasVoted(vote: VotePersonneComplet) { 166 return !isNullDateString(vote.time); 167 } 168 169 isVoteDeletable(vote: VotePersonneComplet) { 170 return !vote.is_locked && this.hasVoted(vote); 171 } 172 173 isVoteDoable(vote: DirtyVote) { 174 return !vote.is_locked && vote.isDirty; 175 } 176 177 onCheckboxInputVote(vote: DirtyVote, choix: number[]) { 178 vote.choix = choix; 179 vote.isDirty = true; 180 } 181 182 radioChecked(choix: Ids) { 183 choix = choix || []; 184 if (choix.length > 0) { 185 return choix[0]; 186 } 187 return null; 188 } 189 190 onRadioInputVote(vote: DirtyVote, choix: number | null) { 191 vote.choix = choix == null ? [] : [choix]; 192 vote.isDirty = true; 193 } 194 195 async save(vote: VotePersonneComplet) { 196 this.loading = true; 197 const data = await C.doVote(vote); 198 this.loading = false; 199 if (data == undefined) return; 200 this.votes = data || []; 201 C.notifications.success = { 202 title: "Vote", 203 message: "Votre vote a bien été enregistré. Merci !" 204 }; 205 } 206 207 async resetVote(vote: VotePersonneComplet) { 208 this.loading = true; 209 const data = await C.resetVote(vote.id); 210 this.loading = false; 211 if (data == undefined) return; 212 this.votes = data || []; 213 C.notifications.success = { 214 title: "Suppression", 215 message: "Votre vote a bien été effacé." 216 }; 217 } 218 } 219 </script> 220 221 <style></style>