From 7ae79dd7ec86517ff0130524402bc5807d7580cb Mon Sep 17 00:00:00 2001
From: nicrausaz <n.crausaz99@gmail.com>
Date: Tue, 23 Apr 2019 13:21:48 +0200
Subject: [PATCH] Ajout position, job et location

---
 .../API/app/Helpers/AccessLevelHelper.php     |   2 +-
 .../Http/Controllers/PositionController.php   |  50 ++++-
 canapGEST/API/resources/lang/fr/http.php      |  12 +-
 canapGEST/API/routes/web.php                  |   8 +-
 canapGEST/Documentation/journal_travail.xlsx  | Bin 12178 -> 12192 bytes
 canapGEST/Site/src/store/modules/modules.js   |   4 +-
 .../src/store/modules/positions/actions.js    |  95 ++++++++
 .../src/store/modules/positions/getters.js    |   0
 .../Site/src/store/modules/positions/index.js |  12 +
 .../src/store/modules/positions/mutations.js  |  11 +
 .../Site/src/store/modules/positions/state.js |   5 +
 .../Site/src/store/modules/user/actions.js    |   4 +
 canapGEST/Site/src/views/OpenJobsView.vue     | 210 +++++++++++++++++-
 13 files changed, 400 insertions(+), 13 deletions(-)
 create mode 100644 canapGEST/Site/src/store/modules/positions/actions.js
 create mode 100644 canapGEST/Site/src/store/modules/positions/getters.js
 create mode 100644 canapGEST/Site/src/store/modules/positions/index.js
 create mode 100644 canapGEST/Site/src/store/modules/positions/mutations.js
 create mode 100644 canapGEST/Site/src/store/modules/positions/state.js

diff --git a/canapGEST/API/app/Helpers/AccessLevelHelper.php b/canapGEST/API/app/Helpers/AccessLevelHelper.php
index 72d30ce..6dda7a4 100644
--- a/canapGEST/API/app/Helpers/AccessLevelHelper.php
+++ b/canapGEST/API/app/Helpers/AccessLevelHelper.php
@@ -55,7 +55,7 @@ class AccessLevelHelper
   {
     $default_access_groups = self::getDefaultAccessGroups();
     $user_alloweds = [];
-    $user_role = 'formateur';
+    $user_role = 'responsable'; // formateur
     $user_groups = explode(',', $user_groups);
 
     foreach ($default_access_groups as $group => $accesses) {
diff --git a/canapGEST/API/app/Http/Controllers/PositionController.php b/canapGEST/API/app/Http/Controllers/PositionController.php
index 9955df2..639b7c4 100644
--- a/canapGEST/API/app/Http/Controllers/PositionController.php
+++ b/canapGEST/API/app/Http/Controllers/PositionController.php
@@ -38,7 +38,7 @@ class PositionController extends Controller
     return DB::table('job')->get();
   }
 
-  public function create()
+  public function createPosition()
   {
     $this->validate($this->request, [
       'position_access_group' => 'required',
@@ -47,7 +47,7 @@ class PositionController extends Controller
       'job_id' => 'required'
     ], [lang::get('validation.required')]);
 
-    $has_permitted_role = AccessLevelHelper::hasPermittedRole($this->user_role, 'formateur');
+    $has_permitted_role = AccessLevelHelper::hasPermittedRole($this->user_role, 'responsable');
 
     $new_position_access_group = $this->request->input('position_access_group');
     $new_position_spot_number = $this->request->input('position_spot_number');
@@ -68,7 +68,7 @@ class PositionController extends Controller
     }
   }
 
-  public function update($id)
+  public function updatePosition($id)
   {
     $this->validate($this->request, [
       'position_access_group' => 'required',
@@ -77,6 +77,8 @@ class PositionController extends Controller
       'job_id' => 'required'
     ], [lang::get('validation.required')]);
 
+    $has_permitted_role = AccessLevelHelper::hasPermittedRole($this->user_role, 'responsable');
+
     $position_access_group = $this->request->input('position_access_group');
     $position_spot_number = $this->request->input('position_spot_number');
     $location_id = $this->request->input('location_id');
@@ -84,7 +86,7 @@ class PositionController extends Controller
 
     $wanted_position_exists = DB::table('position')->where('position_id', $id)->exists();
 
-    if ($wanted_position_exists) {
+    if ($wanted_position_exists && $has_permitted_role) {
       DB::table('position')->where('position_id', $id)->update([
         "position_access_group" => $position_access_group,
         "position_spot_number" => $position_spot_number,
@@ -96,4 +98,44 @@ class PositionController extends Controller
       return abort(403, lang::get('http.unauthorized'));
     }
   }
+
+  public function createLocation () {
+    $this->validate($this->request, [
+      'location_site' => 'required'
+    ], [lang::get('validation.required')]);
+
+    $has_permitted_role = AccessLevelHelper::hasPermittedRole($this->user_role, 'responsable');
+
+    $new_location = $this->request->input('location_site');
+
+    if ($has_permitted_role) {
+      $inserted_id = DB::table('location')->insertGetId([
+        "location_site" => $new_location
+      ]);
+      return ["message" => lang::get('http.success.created.location'), "id" => $inserted_id];
+    } else {
+      return abort(403, lang::get('http.unauthorized'));
+    }
+  }
+
+  public function createJob () {
+    $this->validate($this->request, [
+      'job_full_value' => 'required'
+    ], [lang::get('validation.required')]);
+
+    $has_permitted_role = AccessLevelHelper::hasPermittedRole($this->user_role, 'responsable');
+
+    $new_value = $this->request->input('job_full_value');
+    $new_short_value = preg_replace('/[0-9]+/', '', preg_replace('/[^A-Za-z0-9\-]/', '', preg_replace('/\s+/', '', $new_value)));
+
+    if ($has_permitted_role) {
+      $inserted_id = DB::table('job')->insertGetId([
+        "job_full_value" => $new_value,
+        "job_short_value" => $new_short_value
+      ]);
+      return ["message" => lang::get('http.success.created.job'), "id" => $inserted_id];
+    } else {
+      return abort(403, lang::get('http.unauthorized'));
+    }
+  }
 }
diff --git a/canapGEST/API/resources/lang/fr/http.php b/canapGEST/API/resources/lang/fr/http.php
index 16ac67c..0d6d194 100644
--- a/canapGEST/API/resources/lang/fr/http.php
+++ b/canapGEST/API/resources/lang/fr/http.php
@@ -7,17 +7,23 @@ return [
     'created' => [
       'marker' => 'Marqueur ajouté',
       'comment' => 'Commentaire ajouté',
-      'position' => 'Offre ajoutée'
+      'position' => 'Offre ajoutée',
+      'location' => 'Lieu ajouté',
+      'job' => 'Métier ajouté'
     ],
     'updated' => [
       'marker' => 'Marqueur modifié',
       'comment' => 'Commentaire modifié',
-      'position' => 'Offre modifiée'
+      'position' => 'Offre modifiée',
+      'location' => 'Lieu modifié',
+      'job' => 'Métier modifié'
     ],
     'deleted' => [
       'marker' => 'Marqueur supprimé',
       'comment' => 'Commentaire supprimé',
-      'position' => 'Offre supprimée'
+      'position' => 'Offre supprimée',
+      'location' => 'Lieu supprimé',
+      'job' => 'Métier supprimé'
     ]
   ]
 ];
diff --git a/canapGEST/API/routes/web.php b/canapGEST/API/routes/web.php
index 1a173ec..816e8e3 100644
--- a/canapGEST/API/routes/web.php
+++ b/canapGEST/API/routes/web.php
@@ -34,12 +34,14 @@ $router->group(['middleware' => 'jwt.auth'], function () use ($router) {
   $router->get('api/status', 'StatusController@getAvailableStatus');
   $router->patch('api/status/applicant/{id:[0-9]+}', 'StatusController@updateApplicantStatus');
 
-  // TODO: Positions
+  // Positions, jobs and locations
   $router->get('api/positions', 'PositionController@getAll');
   $router->get('api/locations', 'PositionController@getAvailableLocations');
   $router->get('api/jobs', 'PositionController@getAvailableJobs');
-  $router->put('api/position', 'PositionController@create');
-  $router->patch('api/position/{id:[0-9]+}', 'PositionController@update');
+  $router->put('api/position', 'PositionController@createPosition');
+  $router->put('api/location', 'PositionController@createLocation');
+  $router->put('api/job', 'PositionController@createJob');
+  $router->patch('api/position/{id:[0-9]+}', 'PositionController@updatePosition');
 
   // Files
   $router->get('api/file/{id:[0-9]+}', 'FilesController@getFile');
diff --git a/canapGEST/Documentation/journal_travail.xlsx b/canapGEST/Documentation/journal_travail.xlsx
index 234422504b4254041759d62af3bbba767030b8a1..303f4116bb7439c513e98329ce515c9bf425199d 100644
GIT binary patch
delta 3689
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

delta 3628
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

diff --git a/canapGEST/Site/src/store/modules/modules.js b/canapGEST/Site/src/store/modules/modules.js
index a0171ef..4c25b2b 100644
--- a/canapGEST/Site/src/store/modules/modules.js
+++ b/canapGEST/Site/src/store/modules/modules.js
@@ -1,7 +1,9 @@
 import moduleUser from './user'
 import moduleApplications from './applications'
+import modulePositions from './positions'
 
 export default {
   moduleUser,
-  moduleApplications
+  moduleApplications,
+  modulePositions
 }
\ No newline at end of file
diff --git a/canapGEST/Site/src/store/modules/positions/actions.js b/canapGEST/Site/src/store/modules/positions/actions.js
new file mode 100644
index 0000000..02d4ea7
--- /dev/null
+++ b/canapGEST/Site/src/store/modules/positions/actions.js
@@ -0,0 +1,95 @@
+import axios from '../../../plugins/axios'
+
+export function getPositions(context) {
+  axios({
+    method: 'get',
+    url: '/positions'
+  })
+    .then(response => {
+      context.commit('setPositions', response.data)
+    })
+}
+
+export function getJobs(context) {
+  axios({
+    method: 'get',
+    url: '/jobs'
+  })
+    .then(response => {
+      context.commit('setJobs', response.data)
+    })
+}
+
+export function getLocations(context) {
+  axios({
+    method: 'get',
+    url: '/locations'
+  })
+    .then(response => {
+      context.commit('setLocations', response.data)
+    })
+}
+
+export function createPosition(context, data) {
+  axios({
+    method: 'put',
+    url: '/position',
+    data: data
+  })
+    .then(response => {
+      console.log(response)
+      // Notif this
+    })
+    .catch(err => {
+      console.log(err)
+      // Notif this
+    })
+}
+
+export function updatePosition(context, data) {
+  axios({
+    method: 'patch',
+    url: '/position/' + data.position_id,
+    data: data
+  })
+    .then(response => {
+      console.log(response)
+      // Notif this
+    })
+    .catch(err => {
+      console.log(err)
+      // Notif this
+    })
+}
+
+export function createJob(context, data) {
+  return new Promise((resolve, reject) => {
+    axios({
+      method: 'put',
+      url: '/job',
+      data: data
+    })
+      .then(response => {
+        resolve(response.data)
+      })
+      .catch(err => {
+        reject(err)
+      })
+  })
+}
+
+export function createLocation(context, data) {
+  return new Promise((resolve, reject) => {
+    axios({
+      method: 'put',
+      url: '/location',
+      data: data
+    })
+      .then(response => {
+        resolve(response.data)
+      })
+      .catch(err => {
+        reject(err)
+      })
+  })
+}
\ No newline at end of file
diff --git a/canapGEST/Site/src/store/modules/positions/getters.js b/canapGEST/Site/src/store/modules/positions/getters.js
new file mode 100644
index 0000000..e69de29
diff --git a/canapGEST/Site/src/store/modules/positions/index.js b/canapGEST/Site/src/store/modules/positions/index.js
new file mode 100644
index 0000000..4b0c8d8
--- /dev/null
+++ b/canapGEST/Site/src/store/modules/positions/index.js
@@ -0,0 +1,12 @@
+import state from './state'
+// import * as getters from './getters'
+import * as mutations from './mutations'
+import * as actions from './actions'
+
+export default {
+  namespaced: true,
+  state,
+  // getters,
+  mutations,
+  actions
+}
\ No newline at end of file
diff --git a/canapGEST/Site/src/store/modules/positions/mutations.js b/canapGEST/Site/src/store/modules/positions/mutations.js
new file mode 100644
index 0000000..62c0af7
--- /dev/null
+++ b/canapGEST/Site/src/store/modules/positions/mutations.js
@@ -0,0 +1,11 @@
+export function setPositions (state, data) {
+  state.positions = data
+}
+
+export function setJobs (state, data) {
+  state.available_jobs = data
+}
+
+export function setLocations (state, data) {
+  state.available_locations = data
+}
\ No newline at end of file
diff --git a/canapGEST/Site/src/store/modules/positions/state.js b/canapGEST/Site/src/store/modules/positions/state.js
new file mode 100644
index 0000000..2b7e85a
--- /dev/null
+++ b/canapGEST/Site/src/store/modules/positions/state.js
@@ -0,0 +1,5 @@
+export default {
+  positions: [],
+  available_jobs: [],
+  available_locations: []
+}
\ No newline at end of file
diff --git a/canapGEST/Site/src/store/modules/user/actions.js b/canapGEST/Site/src/store/modules/user/actions.js
index 9be3e70..2afcd0e 100644
--- a/canapGEST/Site/src/store/modules/user/actions.js
+++ b/canapGEST/Site/src/store/modules/user/actions.js
@@ -16,6 +16,10 @@ export function login() {
 }
 
 export function logout() {
+  axios({
+    method: 'get',
+    url: '/auth/logout'
+  })
   localStorage.removeItem('stored_token');
 }
 
diff --git a/canapGEST/Site/src/views/OpenJobsView.vue b/canapGEST/Site/src/views/OpenJobsView.vue
index 60d6277..55b3cc3 100644
--- a/canapGEST/Site/src/views/OpenJobsView.vue
+++ b/canapGEST/Site/src/views/OpenJobsView.vue
@@ -1,12 +1,220 @@
 <template>
   <div id="openjobs-view">
     <h1>Places ouvertes</h1>
+    <template>
+      <div>
+        <v-toolbar flat color="white">
+          <v-spacer></v-spacer>
+          <v-dialog v-model="dialog" max-width="600px">
+            <template v-slot:activator="{ on }">
+              <v-btn color="primary" dark class="mb-2" v-on="on">Ajouter</v-btn>
+            </template>
+            <v-card>
+              <v-card-title>
+                <span class="headline">{{ modalTitle }}</span>
+              </v-card-title>
+
+              <v-card-text>
+                <v-container grid-list-md>
+                  <v-layout wrap>
+                    <!-- <v-text-field
+                        v-model="editedItem.job_short_value"
+                        label="Clé métier"
+                        readonly
+                        disabled
+                    ></v-text-field>-->
+                    <v-flex xs12 sm8 v-if="addJob">
+                      <v-text-field v-model="newJob.job_full_value" label="Nouveau métier"></v-text-field>
+                    </v-flex>
+                    <v-flex xs12 sm5 v-else>
+                      <v-select
+                        :items="$store.state.modulePositions.available_jobs"
+                        label="Métier"
+                        item-text="job_full_value"
+                        item-value="job_id"
+                        v-model="editedItem.job_id"
+                      ></v-select>
+                    </v-flex>
+                    <v-flex xs12 sm1>
+                      <v-btn flat icon color="primary" @click="addNewJob" v-if="!addJob">
+                        <v-icon>add</v-icon>
+                      </v-btn>
+                      <v-btn flat color="primary" @click="addNewJob" v-else>Confirmer</v-btn>
+                    </v-flex>
+                    <v-flex xs12 sm8 v-if="addLocation">
+                      <v-text-field v-model="newLocation.location_site" label="Nouveau lieu"></v-text-field>
+                    </v-flex>
+                    <v-flex xs12 sm5 v-else>
+                      <v-select
+                        :items="$store.state.modulePositions.available_locations"
+                        label="Lieu"
+                        item-text="location_site"
+                        item-value="location_id"
+                        v-model="editedItem.location_id"
+                      ></v-select>
+                    </v-flex>
+                    <v-flex xs12 sm1>
+                      <v-btn flat icon color="primary" @click="AddNewLocation" v-if="!addLocation">
+                        <v-icon>add</v-icon>
+                      </v-btn>
+                      <v-btn flat color="primary" @click="AddNewLocation" v-else>Confirmer</v-btn>
+                    </v-flex>
+
+                    <v-flex xs12 sm6>
+                      <v-text-field
+                        v-model="editedItem.position_access_group"
+                        label="Groupe d'accès"
+                      ></v-text-field>
+                    </v-flex>
+                    <v-flex xs12 sm6>
+                      <v-text-field
+                        v-model="editedItem.position_spot_number"
+                        label="Nombre de places"
+                        type="number"
+                        min="0"
+                      ></v-text-field>
+                    </v-flex>
+                  </v-layout>
+                </v-container>
+              </v-card-text>
+
+              <v-card-actions>
+                <v-spacer></v-spacer>
+                <v-btn color="primary" flat @click="close">Annuler</v-btn>
+                <v-btn color="green" flat @click="save">Confirmer</v-btn>
+              </v-card-actions>
+            </v-card>
+          </v-dialog>
+        </v-toolbar>
+        <v-data-table :headers="headers" :items="$store.state.modulePositions.positions">
+          <template v-slot:items="props">
+            <td>{{ props.item.job_full_value }}</td>
+            <td>{{ props.item.location_site }}</td>
+            <td>{{ props.item.position_access_group }}</td>
+            <td>{{ props.item.position_spot_number }}</td>
+            <td>
+              <v-icon small class="mr-2" @click="editItem(props.item)">edit</v-icon>
+              <v-icon small @click="deleteItem(props.item)">delete</v-icon>
+            </td>
+          </template>
+        </v-data-table>
+      </div>
+    </template>
   </div>
 </template>
 
 <script>
 export default {
-  name: 'openjobs-view'
+  name: 'openjobs-view',
+  data() {
+    return {
+      dialog: false,
+      addJob: false,
+      addLocation: false,
+      headers: [
+        { text: 'Métier', value: 'job_full_value' },
+        { text: 'Lieu', value: 'location_site' },
+        { text: 'Groupe d\'accès', value: 'position_access_group' },
+        { text: 'Nombre de places', value: 'position_spot_number' },
+        { text: 'Actions', value: 'actions', sortable: false }
+      ],
+      editedIndex: -1,
+      editedItem: {
+        job_id: -1,
+        location_id: -1,
+        position_access_group: '',
+        position_spot_number: 0
+      },
+      defaultItem: {
+        job_id: -1,
+        location_id: -1,
+        position_access_group: '',
+        position_spot_number: 0
+      },
+      newJob: {
+        job_full_value: '',
+        job_short_value: ''
+      },
+      newLocation: {
+        location_site: ''
+      }
+    }
+  },
+
+  computed: {
+    modalTitle() {
+      return this.editedIndex === -1 ? 'Nouvelle place' : 'Edition'
+    }
+  },
+
+  watch: {
+    dialog(val) {
+      val || this.close()
+    }
+  },
+  created() {
+    this.loadData()
+  },
+  methods: {
+    loadData() {
+      this.$store.dispatch('modulePositions/getPositions')
+      this.$store.dispatch('modulePositions/getJobs')
+      this.$store.dispatch('modulePositions/getLocations')
+    },
+    editItem(item) {
+      this.editedIndex = item.position_id
+      this.editedItem = Object.assign({}, item)
+      this.dialog = true
+    },
+    deleteItem(item) {
+      if (confirm('Voulez-vous vraiment supprimer cette offre ?')) {
+        // dispatch delete item.position_id
+        console.log(item.position_id)
+      }
+    },
+    close() {
+      this.dialog = false
+      setTimeout(() => {
+        this.editedItem = Object.assign({}, this.defaultItem)
+        this.editedIndex = -1
+        this.addJob = false
+        this.addLocation = false
+      }, 300)
+    },
+    save() {
+      if (this.editedIndex > -1) {
+        // Edit
+        this.$store.dispatch('modulePositions/updatePosition', this.editedItem)
+      } else {
+        // create
+        this.$store.dispatch('modulePositions/createPosition', this.editedItem)
+      }
+      this.close()
+      this.loadData()
+    },
+    addNewJob() {
+      if (this.addJob) {
+        this.$store.dispatch('modulePositions/createJob', this.newJob).then(response => {
+          this.$store.dispatch('modulePositions/getJobs')
+          this.addJob = false
+          this.editedItem.job_id = response.id
+        })
+      }
+      this.addJob = true
+      this.addLocation = false
+    },
+    AddNewLocation() {
+      if (this.addLocation) {
+        this.$store.dispatch('modulePositions/createLocation', this.newLocation).then(response => {
+          this.$store.dispatch('modulePositions/getLocations')
+          this.addLocation = false
+          this.editedItem.location_id = response.id
+        })
+      }
+      this.addLocation = true
+      this.addJob = false
+    }
+  }
 }
 </script>
 
-- 
GitLab