Rule.vue 12.6 KB
Newer Older
1
<template>
David Foucher's avatar
David Foucher committed
2
  <div id="Rule">
3 4 5 6 7 8 9 10 11 12 13 14 15 16
    <div v-if="!isLoading">
      <div class="mb-5">
        <div class="row mb-3">
          <div  class="col-md-6 col-sm-12 col-xs-12">
            <h5>
              <span style="vertical-align:-30%" >{{ displayedName }}</span>
            </h5>
            <a :href="'#'+name+'#modified'" v-if="this.modification_count" @click="displayModification()" id="modification_link">({{ this.modification_count }} modification<span v-if="this.modification_count > 1">s</span> en cours)</a>
          </div>
          <div class="col-md-6 col-sm-12 col-xs-12">
            <h4 v-if="isEditMode" class="pull-right"><em>Modification de la règle</em></h4>
            <input v-else v-b-modal.auth-modal type="button" class="main-button btn pull-right" value="Soumettre une modification"/>
            <!-- TODO: show gitlab link of modification if exists -->
          </div>
17
        </div>
18 19 20 21
        <div v-show="!isEditMode" class="row">
          <div class="col-md-12">
            <span v-html="printRulePath"></span>
          </div>
22 23
        </div>
      </div>
24 25 26 27
      <div v-if="viewModification">
        <ul v-for="modification in modification_list" :key="modification.id">
          <Modification :modification="modification"/>
        </ul>
28
      </div>
29 30 31 32
      <div v-show="!isEditMode && !viewModification">
        <ul>
          <TreeItem class="item" :item="this.ruleTree" :rootElement="true" :rulePath="this.rulePath"></TreeItem>
        </ul>
33
      </div>
34
      <div v-show="isEditMode">
35 36 37 38 39
        <div class="row mb-3">
          <div class="col-md-6 pl-0">
            <input @click="closeEdit" type="button" class="btn btn-outline-danger pull-left" value="Annuler"/>
          </div>
          <div class="col-md-6 pr-0">
40
            <button v-b-modal.mail-modal class="btn btn-outline-success pull-right">Enregistrer</button>
41
          </div>
42
        </div>
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
        <div class="container">
          <div class="row mb-3">
            <label for="content"><u>Contenu de la règle</u></label>
            <textarea-autosize id="content" v-model="content" class="rule-modification-text" :class="{editErrorClass: error_flags.notModified }"></textarea-autosize>
            <span v-if="error_flags.notModified" class="text-danger font-weight-light">Aucune modification n'a été renseignée</span>
          </div>
          <div class="row mb-3">
            <div class="col-md-6 pl-0">
              <input @click="closeEdit" type="button" class="btn btn-outline-danger pull-left" value="Annuler"/>
            </div>
            <div class="col-md-6 pr-0">
              <button v-b-modal.mail-modal class="btn btn-outline-success pull-right">Enregistrer</button>
            </div>
          </div>
        </div>
58
        <b-modal id="auth-modal" title="Authentifiez-vous pour soumettre une modification">
59
          <label for="contributor_email" class="pb-2"><u>Votre email</u> *</label><br>
60 61
          <input id="contributor_email" v-model="auth.email" type="text" :class="{editErrorClass: (error_flags.badUser || error_flags.noUser)}" class="form-control" style="border: 1px solid #bfbfbf; border-radius: 2px;"/><br>
          <span v-if="error_flags.noUser" class="mt-0 text-danger font-weight-light">Ce champ est obligatoire</span><br>
62
          <label for="contributor_passwd" class="pb-2"><u>Votre mot de passe</u> *</label><br>
63 64 65 66
          <input id="contributor_passwd" v-model="auth.password" type="password" :class="{editErrorClass: (error_flags.badUser || error_flags.noPass)}" class="form-control" style="border: 1px solid #bfbfbf; border-radius: 2px;"/><br>
          <span v-if="error_flags.badUser" class="text-danger font-weight-light">Cet utilisateur n'est pas autorisé à soumettre des modifications<br></span>
          <span v-if="error_flags.noPass" class="text-danger font-weight-light">Ce champ est obligatoire<br></span>
          <span v-if="!error_flags.noUser && !error_flags.noPass" class="font-weight-light">* Champs obligatoires<br></span>
67 68 69 70 71 72 73
          <template v-slot:modal-footer>       
            <input @click="auth_to_edit" type="button" class="btn btn-outline-success pull-right" value="Suivant"/>
          </template>
        </b-modal>

        <b-modal id="mail-modal" title="Soumettre votre modification">
          <label for="comment" class="mb-2"><u>Résumé de la modification</u> * </label>
74 75 76
          <textarea id="comment" v-model="comment" :class="{editErrorClass: error_flags.noResume}" rows="3"></textarea>
          <span v-if="error_flags.noResume" class="text-danger font-weight-light">Ce champ est obligatoire</span>
          <span v-if="!error_flags.noUser && !error_flags.noResume" class="font-weight-light">* Champ obligatoire</span>
77 78 79 80 81
          <template v-slot:modal-footer>       
            <input @click="save" type="button" class="btn btn-outline-success pull-right" value="Enregistrer"/>
          </template>
        </b-modal>

82 83
      </div>
    </div>
84 85 86
    <div v-else class="text-center loading-gif">
      <img src="./../assets/images/loading.gif" alt="loading...">
    </div>    
87
  </div>
88 89
</template>
<script>
90
  import TreeItem from './TreeItem.vue';
91
  import Modification from './Modification.vue';
92 93 94 95 96 97 98

  function Node(name) {
    this.name = name;
    this.parent = null;
    this.children = [];
  }

David Foucher's avatar
David Foucher committed
99
  export default {
100
    name: 'Rule',
101 102
    components: {
      TreeItem,
103
      Modification
104
    },
105
    props: ['name', 'data', 'path', 'printRulePath', 'rulePath'],
106 107
    data: function(){
      return {
108
        isLoading: true,
109
        ruleData: this.data,
110
        modification_list: [],
111 112
        content: '',
        comment: '',
113
        isEditMode: '',
114
        viewModification: false,
115 116 117 118 119 120 121 122 123 124 125 126
        auth: {
          email: '',
          password: '',
          file: this.rulePath
        },
        error_flags: {
          badUser: false,
          notModified: false,
          noUser: false,
          noPass: false,
          noResume: false,
        },
127 128
      }
    },
129
    beforeMount: function() {
130
      this.loadInProgressModification();
131 132 133
      if (decodeURI(window.location.hash).split('#').pop() == "modified") { 
        this.displayModification();
      }
134
    },
135
    computed: {
136 137 138 139
      displayedName: function () {
        return this.name.split('.')[0];
      },
      modification_count: function () {
140
        return Object.keys(this.modification_list).length;
141
      },
142 143 144 145 146 147 148
      ruleTree: function() {
        return this.toTree(this.ruleData.split('\n'));
      },
      ruleToEdit: function() {
        return this.ruleData;
      },
    },
149
    methods: {
150 151
      loadInProgressModification: function () {
        this.$http
152
          .get('/source/modified?branch='+encodeURIComponent(this.displayedName))
153
          .then(response => {
154
            this.modification_list = response.body;
155
            this.isLoading = false;
156 157 158 159
            return true;
          }, response => {
            if(response.status == 500) this.modification_list = {};
            return false;
160 161
          })
      },
162
      auth_to_edit: function () {
163 164
        this.error_flags.noUser = false;
        this.error_flags.noPass = false;
165

166 167
        if (this.auth.email== '' ) {
          this.error_flags.noUser = true;
168
        }
169 170
        if (this.auth.password== '' ) {
          this.error_flags.noPass = true;
171 172
          return false;
        }
173 174
        this.$http
          .post('/authentification', this.auth)
175
          .then(() => {
176 177
            this.viewModification = false;
            this.$parent.collapsed = true;
178 179 180 181 182
            this.content = this.ruleToEdit;
            this.isEditMode=!this.isEditMode;
            this.$bvModal.hide("auth-modal");
          }, error => { 
            if (error.status == 401) {
183 184 185
              this.error_flags.badUser = true;
              this.error_flags.noUser = false;
              this.error_flags.noResume = false;
186 187 188
            }
            else if (error.status == 422) {
              if (error.body.args == "`user` est vide") {
189 190
                this.error_flags.noUser = true;
                this.error_flags.badUser = false;
191
              } else if (error.body.args == "`pass` est vide") {
192 193
                this.error_flags.noResume = true;
                this.error_flags.noUser = false;
194
              }
195
              this.error_flags.badUser = false;
196
            }
197
          });
198 199 200 201
      },
      closeEdit: function () {
        this.content = this.ruleToEdit;
        this.comment = "";
202
        this.auth = "";
203
        this.isEditMode=!this.isEditMode;
204 205 206 207 208 209 210
        this.error_flags = {
          badUser: false,
          notModified: false,
          noUser: false,
          noPass: false,
          noResume: false,
        };
211 212
      },
      save: function() {
213
        console.log('test');
214
        this.ruleData = this.content
215
        if (this.comment == '') {
216
          this.error_flags.noResume = true;
217 218
          return false;
        }
219
        const postData = {
220 221
          author_email:this.auth.email,
          author_name:this.auth.email.split("@")[0],
222
          title: this.displayedName,
223 224
          comment: this.comment,
          content: this.content,
225
          filename: 'trefle/config/rules/' + this.path
226 227 228 229 230
        }

        this.$http
          .post('/source/save', postData)
          .then(response => {
231 232 233 234 235
            var commit = {}
            commit.url = 'https://beta.pole-emploi.fr/open-source/trefle/commit/' + response.id
            commit.title = response.title
            this.$parent.rules[this.name]['data'] = this.data
            this.$parent.rules[this.name]['gitlab'] = {'commit': commit}
236 237
            this.$bvModal.hide("mail-modal");
            return this.isEditMode=!this.isEditMode            
238 239
          }, error => {
              if(error.status == 304){
240 241 242 243
                this.error_flags.notModified = true;
                this.error_flags.noUser = false;
                this.error_flags.noResume = false;
                this.error_flags.badUser = false;
244
                this.$bvModal.hide("mail-modal");
245
              } 
246
              else if (error.status == 401) {
247 248 249 250
                this.error_flags.badUser = true;
                this.error_flags.noUser = false;
                this.error_flags.notModified = false;
                this.error_flags.noResume = false;
251 252 253
              }
              else if (error.status == 422) {
                if (error.body.args == "`author_email` est vide") {
254 255
                  this.error_flags.noUser = true;
                  this.error_flags.badUser = false;
256
                } else if (error.body.args == "`comment` est vide") {
257 258
                  this.error_flags.noResume = true;
                  this.error_flags.noUser = false;
259
                }
260 261
                this.error_flags.badUser = false;
                this.error_flags.notModified = false;
262 263
              }
              return false;
264 265 266
          });
      },
      toTree: function (lines) { // eslint-disable-line no-unused-vars
267
        var root= new Node(this.name.split('.')[0]);
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
        var currentIndent=-1;
        var currentNode=root;
        // we scan the rules line by line
        for (var i=0;i<lines.length;i++) {
          var line=lines[i];
          if (line.match(/^\s*$/)) continue; // Empty line, skip
          var indent=line.search(/\S|$/); // number of indenting spaces
          var newNode= new Node(line.trim());
          if (indent>currentIndent) { // New child
            // Set the new node parent
            newNode.parent=currentNode;
            // attach the new node to its parent
            currentNode.children.push(newNode);
            // Set the new current node
            currentNode=newNode;
            currentIndent=indent;
          } else if (indent<currentIndent){
            // Move up in the tree
            var level=currentIndent-indent;
            for (var j=0;j<level/4;j++) {
              // up one level
              currentNode=currentNode.parent;
              currentIndent=currentIndent-4;
            }
            newNode.parent=currentNode.parent;
            currentNode.parent.children.push(newNode); // Add a sibbling
            currentNode=newNode;
          } else {
            // Add as sibbling
            newNode.parent=currentNode.parent;
            currentNode.parent.children.push(newNode);
            currentNode=newNode;
          }
        }
        return root;
303 304 305 306
      },
      displayModification: function () {
        this.viewModification = !this.viewModification;
        this.$parent.collapsed = true;
307
        this.isEditMode = false;
308
      }
309
    },
David Foucher's avatar
David Foucher committed
310 311
  }
</script>
312
<style scoped>
313 314 315
.editErrorClass {
  color: #dc3545;
  border: 1px solid #dc3545;
316 317
  border-radius:3px;
}
318 319 320
.rule-modification-text {
  font-family: 'Courier New', Courier, monospace;
}
321 322 323 324 325

textarea {
  border: 1px solid #bfbfbf;
  border-radius: 2px;
}
326

327 328 329 330 331 332 333 334 335
#modification_link {
  cursor:pointer;
  color:blue;
}

#modification_link:hover {
  text-decoration: underline;
}

336
</style>