comparison client/src/components/usermanagement/Userdetail.vue @ 1558:0ded4c56978e

refac: component filestructure. remove admin/map hierarchy
author Thomas Junk <thomas.junk@intevation.de>
date Wed, 12 Dec 2018 09:22:20 +0100
parents client/src/components/admin/usermanagement/Userdetail.vue@a679f765d7b0
children f2d24dceecc7
comparison
equal deleted inserted replaced
1557:62171cd9a42b 1558:0ded4c56978e
1 <template>
2 <div class="userdetails mt-3 shadow fadeIn animated card">
3 <h6
4 class="mb-0 py-2 px-3 border-bottom d-flex text-info align-items-center"
5 >
6 {{ this.cardHeader }}
7 <span @click="closeDetailview" class="closebutton">
8 <font-awesome-icon icon="times"></font-awesome-icon>
9 </span>
10 </h6>
11 <div class="card-body">
12 <form @submit.prevent="save" class="ml-3">
13 <div class="formfields">
14 <div v-if="currentUser.isNew" class="form-group row">
15 <label for="user"> <translate>Username</translate> </label>
16 <input
17 type="user"
18 :placeholder="userNamePlaceholder"
19 class="form-control form-control-sm"
20 id="user"
21 aria-describedby="userHelp"
22 v-model="currentUser.user"
23 />
24 <div v-show="errors.user" class="text-danger">
25 <small>
26 <font-awesome-icon
27 icon="exclamation-triangle"
28 ></font-awesome-icon>
29 {{ errors.user }}
30 </small>
31 </div>
32 </div>
33 <div class="form-group row">
34 <label for="country"> <translate>Country</translate> </label>
35 <select
36 class="form-control form-control-sm"
37 v-on:change="validateCountry"
38 v-model="currentUser.country"
39 >
40 <option disabled value>
41 <translate>Please select one</translate>
42 </option>
43 <option
44 v-for="country in countries"
45 v-bind:value="country"
46 v-bind:key="country"
47 >{{ country }}</option
48 >
49 </select>
50 <div v-show="errors.country" class="text-danger">
51 <small>
52 <font-awesome-icon
53 icon="exclamation-triangle"
54 ></font-awesome-icon>
55 {{ errors.country }}
56 </small>
57 </div>
58 </div>
59 <div class="form-group row">
60 <label for="email"> <translate>Email address</translate> </label>
61 <input
62 type="email"
63 v-on:change="validateEmailaddress"
64 class="form-control form-control-sm"
65 id="email"
66 aria-describedby="emailHelp"
67 v-model="currentUser.email"
68 />
69 <div v-show="errors.email" class="text-danger">
70 <small>
71 <font-awesome-icon
72 icon="exclamation-triangle"
73 ></font-awesome-icon>
74 {{ errors.email }}
75 </small>
76 </div>
77 </div>
78 <div class="form-group row">
79 <label for="role"> <translate>Role</translate> </label>
80 <select
81 class="form-control form-control-sm"
82 v-on:change="validateRole"
83 v-model="currentUser.role"
84 >
85 <option disabled value>
86 <translate>Please select one</translate>
87 </option>
88 <option value="sys_admin">
89 <translate>Sysadmin</translate>
90 </option>
91 <option value="waterway_admin">
92 <translate>Waterway Admin</translate>
93 </option>
94 <option value="waterway_user">
95 <translate>Waterway User</translate>
96 </option>
97 </select>
98 <div v-show="errors.role" class="text-danger">
99 <small>
100 <font-awesome-icon
101 icon="exclamation-triangle"
102 ></font-awesome-icon>
103 {{ errors.role }}
104 </small>
105 </div>
106 </div>
107 <div class="form-group row">
108 <PasswordField
109 @fieldchange="passwordChanged"
110 :placeholder="passwordPlaceholder"
111 :label="passwordLabel"
112 :passworderrors="errors.password"
113 ></PasswordField>
114 </div>
115 <div class="form-group row">
116 <PasswordField
117 @fieldchange="passwordReChanged"
118 :placeholder="passwordRePlaceholder"
119 :label="passwordReLabel"
120 :passworderrors="errors.passwordre"
121 ></PasswordField>
122 </div>
123 </div>
124 <div>
125 <button
126 type="submit"
127 :disabled="submitted"
128 class="shadow-sm btn btn-info submit-button"
129 >
130 <translate>Submit</translate>
131 </button>
132 </div>
133 <div
134 v-if="currentUser.role != 'waterway_user'"
135 class="form-group row d-flex flex-row justify-content-start mailbutton"
136 >
137 <a @click="sendTestMail" class="btn btn-light">
138 <font-awesome-icon icon="paper-plane"></font-awesome-icon>
139 <translate>Send testmail</translate>
140 </a>
141 <div v-if="mailsent"><translate>Mail was sent</translate></div>
142 </div>
143 </form>
144 </div>
145 </div>
146 </template>
147
148 <style lang="scss" scoped>
149 .submit-button {
150 position: absolute;
151 right: $offset;
152 bottom: $offset;
153 }
154 .mailbutton {
155 width: 12vw;
156 position: absolute;
157 left: $large-offset;
158 bottom: 0;
159 }
160
161 .formfields {
162 width: 60%;
163 }
164
165 .userdetails {
166 height: 600px;
167 margin-top: $offset;
168 margin-left: $offset;
169 margin-right: $offset;
170 }
171
172 form {
173 font-size: $smaller;
174 }
175 </style>
176
177 <script>
178 /* This is Free Software under GNU Affero General Public License v >= 3.0
179 * without warranty, see README.md and license for details.
180 *
181 * SPDX-License-Identifier: AGPL-3.0-or-later
182 * License-Filename: LICENSES/AGPL-3.0.txt
183 *
184 * Copyright (C) 2018 by via donau
185 * – Österreichische Wasserstraßen-Gesellschaft mbH
186 * Software engineering by Intevation GmbH
187 *
188 * Author(s):
189 * Thomas Junk <thomas.junk@intevation.de>
190 */
191 import { HTTP } from "../../lib/http";
192 import { displayError } from "../../lib/errors.js";
193 import { mapState } from "vuex";
194 import PasswordField from "./Passwordfield";
195
196 const emptyErrormessages = () => {
197 return {
198 email: "",
199 country: "",
200 role: "",
201 password: "",
202 passwordre: ""
203 };
204 };
205
206 const isEmailValid = email => {
207 /**
208 *
209 * For convenience purposes the same regex used as in the go code
210 * cf. types.go
211 *
212 */
213 // eslint-disable-next-line
214 return /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test(
215 email
216 );
217 };
218
219 const violatedPasswordRules = password => {
220 return (
221 // rules according to issue 70
222 password.length < 7 ||
223 /\W/.test(password) == false ||
224 /\d/.test(password) == false
225 );
226 };
227
228 export default {
229 name: "userdetail",
230 components: {
231 PasswordField
232 },
233 data() {
234 return {
235 mailsent: false,
236 passwordLabel: this.$gettext("Password"),
237 passwordReLabel: this.$gettext("Repeat Password"),
238 passwordPlaceholder: this.$gettext("password"),
239 passwordRePlaceholder: this.$gettext("password again"),
240 password: "",
241 passwordre: "",
242 currentUser: {},
243 path: null,
244 submitted: false,
245 errors: {
246 email: "",
247 country: "",
248 role: "",
249 password: "",
250 passwordre: ""
251 }
252 };
253 },
254 mounted() {
255 this.currentUser = { ...this.user };
256 this.path = this.user.name;
257 },
258 watch: {
259 user() {
260 this.currentUser = { ...this.user };
261 this.path = this.user.name;
262 this.clearPassword();
263 this.clearErrors();
264 }
265 },
266 computed: {
267 cardHeader() {
268 if (this.currentUser.isNew) return "N.N";
269 return this.currentUser.user;
270 },
271 userNamePlaceholder() {
272 if (this.currentUser.isNew) return "N.N";
273 return "";
274 },
275 ...mapState("application", ["countries"]),
276 user() {
277 return this.$store.getters["usermanagement/currentUser"];
278 },
279 isFormValid() {
280 return (
281 isEmailValid(this.currentUser.email) &&
282 this.currentUser.country &&
283 this.password === this.passwordre &&
284 (this.password === "" || !violatedPasswordRules(this.password))
285 );
286 }
287 },
288 methods: {
289 sendTestMail() {
290 if (this.mailsent) return;
291 HTTP.get("/testmail/" + this.currentUser.user, {
292 headers: {
293 "X-Gemma-Auth": localStorage.getItem("token"),
294 "Content-type": "text/xml; charset=UTF-8"
295 }
296 })
297 .then(() => {
298 this.mailsent = true;
299 })
300 .catch(error => {
301 this.loginFailed = true;
302 this.submitted = false;
303 const { status, data } = error.response;
304 displayError({
305 title: this.$gettext("Backend Error"),
306 message: `${status}: ${data.message || data}`
307 });
308 });
309 },
310 passwordChanged(value) {
311 this.password = value;
312 this.validatePassword();
313 },
314 passwordReChanged(value) {
315 this.passwordre = value;
316 this.validatePassword();
317 },
318 clearErrors() {
319 this.errors = emptyErrormessages();
320 },
321 clearPassword() {
322 this.password = "";
323 this.passwordre = "";
324 },
325 closeDetailview() {
326 this.$store.commit("usermanagement/clearCurrentUser");
327 this.$store.commit("usermanagement/setUserDetailsInvisible");
328 },
329 validateCountry() {
330 this.errors.country = this.currentUser.country
331 ? ""
332 : this.$gettext("Please choose a country");
333 },
334 validateRole() {
335 this.errors.role = this.currentUser.role
336 ? ""
337 : this.$gettext("Please choose a role");
338 },
339 validatePassword() {
340 this.errors.passwordre =
341 this.password === this.passwordre
342 ? ""
343 : this.$gettext("Passwords do not match!");
344 this.errors.password =
345 this.password === "" || !violatedPasswordRules(this.password)
346 ? ""
347 : this.$gettext(
348 "Password should at least be 8 char long including 1 digit and 1 special char like $"
349 );
350 },
351 validateEmailaddress() {
352 this.errors.email = isEmailValid(this.currentUser.email)
353 ? ""
354 : this.$gettext("invalid email");
355 },
356 validate() {
357 this.validateCountry();
358 this.validateRole();
359 this.validatePassword();
360 this.validateEmailaddress();
361 },
362 save() {
363 this.validate();
364 if (!this.isFormValid) return;
365 if (this.password) this.currentUser.password = this.password;
366 this.submitted = true;
367 this.$store
368 .dispatch("usermanagement/saveCurrentUser", {
369 path: this.user.user,
370 user: this.currentUser
371 })
372 .then(() => {
373 this.submitted = false;
374 this.$store.dispatch("usermanagement/loadUsers").catch(error => {
375 const { status, data } = error.response;
376 displayError({
377 title: this.$gettext("Backend Error"),
378 message: `${status}: ${data.message || data}`
379 });
380 });
381 })
382 .catch(error => {
383 this.submitted = false;
384 const { status, data } = error.response;
385 displayError({
386 title: this.$gettext("Error while saving user"),
387 message: `${status}: ${data.message || data}`
388 });
389 });
390 }
391 }
392 };
393 </script>