Newer
Older
Sébastien DA ROCHA
committed
<template>
<div class="fourteen wide column">
<div :class="{ active: loading }" class="ui inverted dimmer">
<div class="ui text loader">
Projet en cours de création. Vous allez être redirigé.
</div>
</div>
Sébastien DA ROCHA
committed
<form id="form-project-edit" class="ui form">
<h1>
<span v-if="action === 'edit'"
>Édition du projet "{{ form.title }}"</span
>
<span v-else-if="action === 'create'">Création d'un projet</span>
</h1>
<div class="ui horizontal divider">INFORMATIONS</div>
<div class="two fields">
<div class="required field">
<label for="title">Titre</label>
<input
type="text"
required
maxlength="128"
name="title"
id="title"
v-model="form.title"
/>
<ul id="errorlist-title" class="errorlist">
<li v-for="error in errors.title" :key="error">
{{ error }}
</li>
</ul>
Sébastien DA ROCHA
committed
</div>
<div class="field">
<label>Illustration du projet</label>
<img
class="ui small image"
id="form-input-file-logo"
:src="
thumbnailFileSrc
? thumbnailFileSrc
: DJANGO_BASE_URL + form.thumbnail
"
/>
<label class="ui icon button" for="thumbnail">
<i class="file icon"></i>
<span class="label">{{
form.thumbnail_name ? form.thumbnail_name : fileToImport.name
}}</span>
</label>
<input
@change="onFileChange"
class="file-selection"
type="file"
accept="image/jpeg, image/png"
style="display: none"
name="thumbnail"
id="thumbnail"
/>
<ul
v-if="errorThumbnail.length"
id="errorlist-thumbnail"
class="errorlist"
>
Sébastien DA ROCHA
committed
</div>
</div>
<div class="field">
<label for="description">Description</label>
<textarea
v-model="form.description"
name="description"
rows="5"
></textarea>
<!-- {{ form.description.errors }} -->
</div>
<div class="ui horizontal divider">PARAMÈTRES</div>
<div class="four fields">
<div class="field">
<label for="archive_feature">Délai avant archivage</label>
<div class="ui right labeled input">
<input
type="number"
min="0"
Sébastien DA ROCHA
committed
style="padding: 1px 2px"
name="archive_feature"
id="archive_feature"
Sébastien DA ROCHA
committed
v-model="form.archive_feature"
/>
<div class="ui label">jour(s)</div>
</div>
<ul
v-if="errors_archive_feature.length"
id="errorlist-achivage"
class="errorlist"
>
<li>
{{ errors_archive_feature[0] }}
</li>
</ul>
Sébastien DA ROCHA
committed
</div>
<div class="field">
<label for="delete_feature">Délai avant suppression</label>
<div class="ui right labeled input">
<input
type="number"
min="0"
Sébastien DA ROCHA
committed
style="padding: 1px 2px"
name="delete_feature"
id="delete_feature"
Sébastien DA ROCHA
committed
v-model="form.delete_feature"
/>
<div class="ui label">jour(s)</div>
</div>
</div>
<div class="required field">
<label for="access_level_pub_feature"
>Visibilité des signalements publiés</label
>
<Dropdown
:options="levelPermissions"
:selected="form.access_level_pub_feature.name"
:selection.sync="form.access_level_pub_feature"
/>
<ul id="errorlist-access_level_pub_feature" class="errorlist">
<li v-for="error in errors.access_level_pub_feature" :key="error">
{{ error }}
</li>
</ul>
Sébastien DA ROCHA
committed
</div>
<div class="required field">
<label for="access_level_arch_feature">
Visibilité des signalements archivés
</label>
<Dropdown
:options="levelPermissions"
:selected="form.access_level_arch_feature.name"
:selection.sync="form.access_level_arch_feature"
/>
<ul id="errorlist-access_level_arch_feature" class="errorlist">
<li v-for="error in errors.access_level_arch_feature" :key="error">
{{ error }}
</li>
</ul>
Sébastien DA ROCHA
committed
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
</div>
</div>
<div class="field">
<div class="ui checkbox">
<input
type="checkbox"
v-model="form.moderation"
name="moderation"
id="moderation"
/>
<label for="moderation">Modération</label>
</div>
<!-- {{ form.moderation.errors }} -->
</div>
<div class="field">
<div class="ui checkbox">
<input
type="checkbox"
v-model="form.is_project_type"
name="is_project_type"
id="is_project_type"
/>
<label for="is_project_type">Est un projet type</label>
</div>
<!-- {{ form.is_project_type.errors }} -->
</div>
<div class="ui divider"></div>
<button @click="postForm" type="button" class="ui teal icon button">
<i class="white save icon"></i> Enregistrer les changements
</button>
</form>
</div>
</template>
<script>
const axios = require("axios");
import Dropdown from "@/components/Dropdown.vue";
import { mapGetters } from "vuex";
axios.defaults.headers.common["X-CSRFToken"] = ((name) => {
var re = new RegExp(name + "=([^;]+)");
var value = re.exec(document.cookie);
Sébastien DA ROCHA
committed
export default {
name: "Project_edit",
components: {
Dropdown,
},
data() {
return {
Sébastien DA ROCHA
committed
action: "create",
levelPermissions: [
{ name: "Utilisateur anonyme", value: "anonymous" },
{ name: "Utilisateur connecté", value: "logged_user" },
{ name: "Contributeur", value: "contributor" },
],
fileToImport: {
name: "Sélectionner une image ...",
size: 0,
},
errors: {
title: [],
access_level_pub_feature: [],
access_level_arch_feature: [],
},
Sébastien DA ROCHA
committed
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
form: {
title: "",
slug: "",
created_on: "",
updated_on: "",
description: "",
moderation: false,
thumbnail: "", // todo : utiliser l'image par défaut
thumbnail_name: "", // todo: delete after getting image in jpg or png instead of data64 (require post to django)
creator: null,
access_level_pub_feature: { name: "", value: "" },
access_level_arch_feature: { name: "", value: "" },
archive_feature: 0,
delete_feature: 0,
nb_features: 0,
nb_published_features: 0,
nb_comments: 0,
nb_published_features_comments: 0,
nb_contributors: 0,
is_project_type: false,
},
thumbnailFileSrc: "",
};
},
computed: {
...mapGetters(["project"]),
Sébastien DA ROCHA
committed
return this.$store.state.configuration.VUE_APP_DJANGO_BASE;
},
},
methods: {
definePageType() {
if (this.$router.history.current.name === "project_create") {
this.action = "create";
} else if (this.$router.history.current.name === "project_edit") {
this.action = "edit";
} else if (this.$router.history.current.name === "project_create_from") {
this.action = "create_from";
}
},
Sébastien DA ROCHA
committed
truncate(n, len) {
let ext = n.substring(n.lastIndexOf(".") + 1, n.length).toLowerCase();
let filename = n.replace("." + ext, "");
Sébastien DA ROCHA
committed
if (filename.length <= len) {
return n;
}
filename = filename.substr(0, len) + (n.length > len ? "[...]" : "");
return filename + "." + ext;
},
validateImgFile(files, handleFile) {
let url = window.URL || window.webkitURL;
let image = new Image();
image.onload = function () {
handleFile(true);
};
image.onerror = function () {
handleFile(false);
};
image.src = url.createObjectURL(files);
URL.revokeObjectURL(image.src);
},
Sébastien DA ROCHA
committed
onFileChange(e) {
// * read image file
const files = e.target.files || e.dataTransfer.files;
const _this = this; //* 'this' is different in onload function
function handleFile(isValid) {
if (isValid) {
_this.fileToImport = files[0]; //* store the file to post later
let reader = new FileReader(); //* read the file to display in the page
reader.onload = function (e) {
_this.thumbnailFileSrc = e.target.result;
};
reader.readAsDataURL(_this.fileToImport);
_this.errorThumbnail = [];
} else {
_this.errorThumbnail.push(
"Transférez une image valide. Le fichier que vous avez transféré n'est pas une image, ou il est corrompu."
);
}
}
if (files.length) {
//* check if file is an image and pass callback to handle file
this.validateImgFile(files[0], handleFile);
}
Sébastien DA ROCHA
committed
},
checkEmpty() {
//* forbid empty fields
if (!this.form.archive_feature) this.form.archive_feature = 0;
if (!this.form.delete_feature) this.form.delete_feature = 0;
},
Sébastien DA ROCHA
committed
goBackNrefresh(slug) {
Promise.all([
this.$store.dispatch("GET_USER_LEVEL_PERMISSIONS"), //* refresh projects permissions
this.$store.dispatch("GET_ALL_PROJECTS"), //* & refresh project list
]).then(() =>
// * go back to project list
this.$router.push({
Sébastien DA ROCHA
committed
name: "project_detail",
params: { slug },
Sébastien DA ROCHA
committed
);
},
postProjectThumbnail(projectSlug) {
//* send img to the backend when feature_type is created
if (this.fileToImport) {
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
let formData = new FormData();
formData.append("file", this.fileToImport);
const url =
this.$store.state.configuration.VUE_APP_DJANGO_API_BASE +
"projects/" +
projectSlug +
"/thumbnail/";
return axios
.put(url, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((response) => {
if (response && response.status === 200) {
this.goBackNrefresh(projectSlug);
}
})
.catch((error) => {
let err_msg =
"Transférez une image valide. Le fichier que vous avez transféré n'est pas une image, ou il est corrompu.";
if (error.response.data[0]) err_msg = error.response.data[0];
this.errorThumbnail.push(err_msg);
throw error;
});
}
Sébastien DA ROCHA
committed
},
if (this.form.archive_feature > this.form.delete_feature) {
this.errors_archive_feature.push(
"Le délais de suppression doit être supérieur au délais d'archivage."
);
for (const key in this.errors) {
if ((key === "title" && this.form[key]) || this.form[key].value) {
this.errors[key] = [];
} else if (!this.errors[key].length) {
this.errors[key].push(
key === "title"
? "Veuillez compléter ce champ."
: "Sélectionnez un choix valide. Ce choix ne fait pas partie de ceux disponibles."
);
document
.getElementById(`errorlist-${key}`)
.scrollIntoView({ block: "end", inline: "nearest" });
return false;
}
}
return true;
},
Sébastien DA ROCHA
committed
async postForm() {
Sébastien DA ROCHA
committed
const projectData = {
title: this.form.title,
description: this.form.description,
access_level_arch_feature: this.form.access_level_arch_feature.value,
access_level_pub_feature: this.form.access_level_pub_feature.value,
archive_feature: this.form.archive_feature,
delete_feature: this.form.delete_feature,
is_project_type: this.form.is_project_type,
moderation: this.form.moderation,
};
if (this.action === "create" || this.action === "create_from") {
Sébastien DA ROCHA
committed
await axios
.post(
`${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/`,
projectData
)
Sébastien DA ROCHA
committed
.then((response) => {
if (response && response.status === 201 && response.data) {
//* send thumbnail after feature_type was created
Sébastien DA ROCHA
committed
this.postProjectThumbnail(response.data.slug);
this.goBackNrefresh(response.data.slug);
}
Sébastien DA ROCHA
committed
}
Sébastien DA ROCHA
committed
})
.catch((error) => {
if (error.response && error.response.data.title[0]) {
this.errors.title.push(error.response.data.title[0]);
}
Sébastien DA ROCHA
committed
throw error;
});
} else if (this.action === "edit") {
await axios
.put(
`${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.project.slug}/`,
projectData
)
.then((response) => {
if (response && response.status === 200) {
//* send thumbnail after feature_type was updated
if (this.fileToImport.size > 0) {
Sébastien DA ROCHA
committed
this.postProjectThumbnail(this.project.slug);
} else {
this.goBackNrefresh(this.project.slug);
}
Sébastien DA ROCHA
committed
}
})
.catch((error) => {
if (error.response && error.response.data.title[0]) {
this.errors.title.push(error.response.data.title[0]);
}
Sébastien DA ROCHA
committed
throw error;
});
}
},
},
created() {
this.definePageType();

Timothee P
committed
console.log(this.action);
Sébastien DA ROCHA
committed
if (this.action === "create") {
this.thumbnailFileSrc = require("@/assets/img/default.png");

Timothee P
committed
} else if (this.action === "edit" || this.action === "create_from") {
Sébastien DA ROCHA
committed
if (!this.project) {
this.$store.dispatch("GET_PROJECT_INFO", this.$route.params.slug);
}

Timothee P
committed
this.form = { ...this.project }; //* create a new object to avoid modifying original one
if (this.action === "create_from") {
this.form.title =
this.project.title +
` (Copie-${new Date()
.toLocaleString()
.slice(0, -3)
.replace(",", "")})`;
this.form.is_project_type = false;
}
Sébastien DA ROCHA
committed
//* transform string values to objects for dropdowns display (could be in a computed)
this.form.access_level_pub_feature = {
name: this.project.access_level_pub_feature,
value: this.levelPermissions.find(
(el) => (el.name === this.project.access_level_pub_feature)
Sébastien DA ROCHA
committed
).value,
};
this.form.access_level_arch_feature = {
name: this.project.access_level_arch_feature,
value: this.levelPermissions.find(
(el) => (el.name === this.project.access_level_arch_feature)
Sébastien DA ROCHA
committed
).value,
};
}
},
};
</script>
<style media="screen">
#form-input-file-logo {
margin-left: auto;
margin-right: auto;
}
.close.icon:hover {
cursor: pointer;
}
</style>