diff --git a/canapGEST/API/app/Helpers/AccessLevelHelper.php b/canapGEST/API/app/Helpers/AccessLevelHelper.php
index 72d30ced68d00831fc3148db2307f75535eb1a99..6dda7a491a0e6cf3726744d537c69933db4a6ef4 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 9955df2c2df708f4d40de19fa6e9dbecbffccd2c..639b7c43679e9dcba9b5cb2031079c926b55ed82 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 16ac67cd62e69be420f7c440977899010609e2ff..0d6d194b0fee24fe83e68c4a7e410b81b2da9680 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 1a173ecb23cac768d6aba44fe8c79dcab81e8037..816e8e3d8692af98f863f693783c53160993c358 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
Binary files a/canapGEST/Documentation/journal_travail.xlsx and b/canapGEST/Documentation/journal_travail.xlsx differ
diff --git a/canapGEST/Site/src/store/modules/modules.js b/canapGEST/Site/src/store/modules/modules.js
index a0171efe3cf363b239b638e82b65c4de396aec64..4c25b2bd05abfc090870c97053c5ae9cd63de577 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 0000000000000000000000000000000000000000..02d4ea7126c15836022f255caeb775a059f947b0
--- /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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
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 0000000000000000000000000000000000000000..4b0c8d847d7bba60a0d61cf502a9ec3fa03c605a
--- /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 0000000000000000000000000000000000000000..62c0af728d31381e9404b57e3cec0943c0f8b43a
--- /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 0000000000000000000000000000000000000000..2b7e85ae8dfa6dccfdd23cbc3cfad9f7c5ecec84
--- /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 9be3e7074e209ba41f5a6a26f0f76bc20925e0b5..2afcd0e07212095f5333183d4343d8dbfdabbf73 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 60d6277e1e817f201dcf8838804a34772db72323..55b3cc36f9e20874cdd7438483254dd3447e80ec 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>