feat: add vuelidate (#3438)
This commit is contained in:
+1
-1
@@ -14,7 +14,7 @@ indent_size = 4
|
||||
[*.blade.php]
|
||||
indent_size = 2
|
||||
|
||||
[*.{js,vue}]
|
||||
[*.{js,vue,scss}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
### New features:
|
||||
|
||||
* Add vue data validations
|
||||
* Add ability to edit activities
|
||||
* Associate a photo to a gift
|
||||
|
||||
|
||||
+2
-1
@@ -64,7 +64,8 @@
|
||||
"vue-i18n": "^8.8",
|
||||
"vue-js-toggle-button": "^1.2.2",
|
||||
"vue-notification": "^1.3.6",
|
||||
"vue-select": "^3.0.2"
|
||||
"vue-select": "^3.0.2",
|
||||
"vuelidate": "^0.7.4"
|
||||
},
|
||||
"snyk": true
|
||||
}
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"/js/manifest.js": "/js/manifest.js?id=7db827d654313dce4250",
|
||||
"/js/vendor.js": "/js/vendor.js?id=6ccef718ebd46e2c4f89",
|
||||
"/js/app.js": "/js/app.js?id=9ed7ed32ce507eff51e4",
|
||||
"/css/app-ltr.css": "/css/app-ltr.css?id=ff41d6c39e7fba5cf37e",
|
||||
"/css/app-rtl.css": "/css/app-rtl.css?id=ee3f6d81111cdc99f35d",
|
||||
"/js/vendor.js": "/js/vendor.js?id=773b341f83f70ee67b38",
|
||||
"/js/app.js": "/js/app.js?id=cb94afd4886fb56d9a38",
|
||||
"/css/app-ltr.css": "/css/app-ltr.css?id=543870c3275cb99afc76",
|
||||
"/css/app-rtl.css": "/css/app-rtl.css?id=26aa1824e819b5990779",
|
||||
"/css/stripe.css": "/css/stripe.css?id=76c70a7b11ae5f38a725",
|
||||
"/js/stripe.js": "/js/stripe.js?id=5e5e1d73338a536f7ce9",
|
||||
"/js/u2f-api.js": "/js/u2f-api.js?id=1948d3efdfd801bed14a"
|
||||
|
||||
@@ -33,10 +33,11 @@
|
||||
<form-input
|
||||
:id="'age'"
|
||||
ref="age"
|
||||
:value="age"
|
||||
v-model="selectedAge"
|
||||
:input-type="'number'"
|
||||
:width="50"
|
||||
:required="true"
|
||||
:validator="$v.selectedAge"
|
||||
/>
|
||||
</div>
|
||||
</form-radio>
|
||||
@@ -86,10 +87,12 @@
|
||||
<form-date
|
||||
:id="'birthdayDate'"
|
||||
ref="birthday"
|
||||
:value="birthdate"
|
||||
v-model="selectedDate"
|
||||
:show-calendar-on-focus="true"
|
||||
:locale="locale"
|
||||
:label="$t('people.information_edit_birthdate_label')"
|
||||
:class="[ dirltr ? 'fl' : 'fr' ]"
|
||||
:validator="$v.selectedDate"
|
||||
/>
|
||||
</div>
|
||||
</form-radio>
|
||||
@@ -112,8 +115,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import { validationMixin } from 'vuelidate';
|
||||
import { required, numeric, helpers } from 'vuelidate/lib/validators';
|
||||
|
||||
const before = (param) =>
|
||||
helpers.withParams(
|
||||
{ type: 'before', date: param },
|
||||
(value) => !helpers.req(value) || moment(value).isBefore(param)
|
||||
);
|
||||
|
||||
export default {
|
||||
|
||||
mixins: [validationMixin],
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
@@ -121,15 +136,11 @@ export default {
|
||||
},
|
||||
days: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [];
|
||||
}
|
||||
default: () => [],
|
||||
},
|
||||
months: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [];
|
||||
}
|
||||
default: () => [],
|
||||
},
|
||||
day: {
|
||||
type: Number,
|
||||
@@ -158,12 +169,35 @@ export default {
|
||||
selectedDate: null,
|
||||
selectedOption: null,
|
||||
selectedOptionSave: null,
|
||||
selectedAge: 0,
|
||||
selectedMonth: 0,
|
||||
selectedDay: 0,
|
||||
hasBirthdayReminder: false
|
||||
};
|
||||
},
|
||||
|
||||
validations() {
|
||||
switch (this.selectedOption) {
|
||||
case 'approximate':
|
||||
return {
|
||||
selectedAge: {
|
||||
required,
|
||||
numeric,
|
||||
}
|
||||
};
|
||||
break;
|
||||
case 'exact':
|
||||
return {
|
||||
selectedDate: {
|
||||
required,
|
||||
before: before(moment())
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir == 'ltr';
|
||||
@@ -173,9 +207,25 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
birthdate(val) {
|
||||
this.selectedDate = val;
|
||||
},
|
||||
|
||||
value(val) {
|
||||
this.selectedOption = val;
|
||||
},
|
||||
|
||||
age(val) {
|
||||
this.selectedAge = val;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.selectedDate = this.birthdate;
|
||||
this.selectedOption = this.value != '' ? this.value : 'unknown';
|
||||
this.selectedOptionSave = this.selectedOption;
|
||||
this.selectedAge = this.age;
|
||||
this.selectedMonth = this.month;
|
||||
this.selectedDay = this.day;
|
||||
this.hasBirthdayReminder = this.reminder;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="pa4-ns ph3 pv2 bb b--gray-monica">
|
||||
<div class="mb3 mb0-ns">
|
||||
<form-checkbox
|
||||
v-model="value"
|
||||
v-model.lazy="deceased"
|
||||
:name="'is_deceased'"
|
||||
:value="true"
|
||||
:dclass="'flex mb2'"
|
||||
@@ -15,7 +15,7 @@
|
||||
{{ $t('people.deceased_mark_person_deceased') }}
|
||||
</template>
|
||||
</form-checkbox>
|
||||
<div v-show="value" :class="[ dirltr ? 'ml4' : 'mr4' ]">
|
||||
<div v-show="deceased" :class="[ dirltr ? 'ml4' : 'mr4' ]">
|
||||
<form-checkbox
|
||||
v-model.lazy="dateKnown"
|
||||
:name="'is_deceased_date_known'"
|
||||
@@ -31,11 +31,13 @@
|
||||
<form-date
|
||||
:id="'deceased_date'"
|
||||
ref="deaceasedday"
|
||||
v-model="date"
|
||||
v-model="selectedDate"
|
||||
:label="$t('people.deceased_date_label')"
|
||||
:show-calendar-on-focus="true"
|
||||
:locale="locale"
|
||||
:validator="$v.selectedDate"
|
||||
/>
|
||||
<div v-show="date != ''" class="mt2">
|
||||
<div v-show="selectedDate != ''" class="mt2">
|
||||
<form-checkbox
|
||||
:name="'add_reminder_deceased'"
|
||||
:value="true"
|
||||
@@ -51,8 +53,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import { validationMixin } from 'vuelidate';
|
||||
import { required, numeric, helpers } from 'vuelidate/lib/validators';
|
||||
|
||||
const before = (param) =>
|
||||
helpers.withParams(
|
||||
{ type: 'before', date: param },
|
||||
(value) => !helpers.req(value) || moment(value).isBefore(param)
|
||||
);
|
||||
|
||||
export default {
|
||||
|
||||
mixins: [validationMixin],
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
@@ -70,10 +84,19 @@ export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
deceased: false,
|
||||
dateKnown: false,
|
||||
selectedDate: null,
|
||||
};
|
||||
},
|
||||
|
||||
validations: {
|
||||
selectedDate: {
|
||||
required,
|
||||
before: before(moment())
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir == 'ltr';
|
||||
@@ -83,8 +106,20 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value(val) {
|
||||
this.deceased = val;
|
||||
},
|
||||
|
||||
date(val) {
|
||||
this.selectedDate = val;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.deceased = this.value;
|
||||
this.dateKnown = this.date != '';
|
||||
this.selectedDate = this.date;
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div :class="{ 'form-group-error': validator && validator.$error }">
|
||||
<datepicker
|
||||
ref="select"
|
||||
:ref-name="'select'"
|
||||
@@ -8,15 +11,21 @@
|
||||
:parse-typed-date="formatTypedValue"
|
||||
:language="locale"
|
||||
:monday-first="mondayFirst"
|
||||
:input-class="'br2 f5 ba b--black-40 pa2 outline-0'"
|
||||
:input-class="inputClass"
|
||||
:typeable="true"
|
||||
:clear-button="true"
|
||||
:show-calendar-on-focus="showCalendarOnFocus"
|
||||
@input="$emit('input', exchangeValue($event))"
|
||||
@selected="update"
|
||||
@clearDate="update('')"
|
||||
@input="onInput($event)"
|
||||
@selected="onSelected($event)"
|
||||
@clearDate="onSelected('')"
|
||||
/>
|
||||
<input :name="id" type="hidden" :value="exchange" />
|
||||
<small v-if="validator && (validator.$error && validator.required !== undefined && !validator.required)" class="error">
|
||||
{{ requiredMessage }}
|
||||
</small>
|
||||
<small v-if="validator && (validator.$error && validator.before !== undefined && !validator.before)" class="error">
|
||||
{{ beforeMessage }}
|
||||
</small>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -44,6 +53,10 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
defaultDate: {
|
||||
type: String,
|
||||
default: '',
|
||||
@@ -55,7 +68,11 @@ export default {
|
||||
showCalendarOnFocus: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
validator: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
@@ -84,6 +101,25 @@ export default {
|
||||
displayFormat() {
|
||||
return 'L';
|
||||
},
|
||||
|
||||
inputClass() {
|
||||
var c = ['br2 f5 ba b--black-40 pa2 outline-0'];
|
||||
if (this.validator) {
|
||||
c.push({ 'error': this.validator.$error });
|
||||
}
|
||||
return c;
|
||||
},
|
||||
|
||||
requiredMessage() {
|
||||
return this.$t('validation.vue.required', { field: this.label });
|
||||
},
|
||||
|
||||
beforeMessage() {
|
||||
return this.$t('validation.vue.max.numeric', {
|
||||
field: this.label,
|
||||
max: this.displayValue(this.validator.$params.before.date)
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -130,7 +166,6 @@ export default {
|
||||
}
|
||||
this.exchange = mdate.format(this.exchangeFormat);
|
||||
}
|
||||
this.$emit('input', this.exchange);
|
||||
},
|
||||
|
||||
updateExchange(date) {
|
||||
@@ -156,8 +191,19 @@ export default {
|
||||
|
||||
focus() {
|
||||
this.$refs.select.$children[0].$refs.select.focus();
|
||||
}
|
||||
},
|
||||
|
||||
onInput(event) {
|
||||
if (this.validator) {
|
||||
this.validator.$touch();
|
||||
}
|
||||
this.$emit('input', this.exchangeValue(event));
|
||||
},
|
||||
|
||||
onSelected(event) {
|
||||
this.update(event);
|
||||
this.onInput(event);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
<style scoped>
|
||||
input {
|
||||
.input {
|
||||
transition: all;
|
||||
transition-duration: 0.2s;
|
||||
border: 1px solid #c4cdd5;
|
||||
}
|
||||
input:focus {
|
||||
|
||||
.input:focus {
|
||||
border: 1px solid #5c6ac4;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div :class="{ 'form-group-error': validator && validator.$error }">
|
||||
<label
|
||||
v-if="title"
|
||||
:for="realid"
|
||||
class="mb2"
|
||||
:class="{ b: required }"
|
||||
:class="{ b: required, error: validator && validator.$error }"
|
||||
>
|
||||
{{ title }}
|
||||
</label>
|
||||
@@ -31,9 +32,20 @@ input:focus {
|
||||
:style="inputStyle"
|
||||
:value="value"
|
||||
:maxlength="maxlength"
|
||||
@input="event => { $emit('input', event.target.value) }"
|
||||
@keyup.enter="event => { $emit('submit', event.target.value) }"
|
||||
@input="onInput($event)"
|
||||
@blur="onBlur($event)"
|
||||
@change="onChange($event)"
|
||||
@keyup.enter="onSubmit($event)"
|
||||
/>
|
||||
<small v-if="validator && (validator.$error && validator.required !== undefined && !validator.required)" class="error">
|
||||
{{ requiredMessage }}
|
||||
</small>
|
||||
<small v-if="validator && (validator.$error && validator.maxLength !== undefined && !validator.maxLength)" class="error">
|
||||
{{ maxLengthMessage }}
|
||||
</small>
|
||||
<small v-if="validator && (validator.$error && validator.url !== undefined && !validator.url)" class="error">
|
||||
{{ urlMessage }}
|
||||
</small>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -49,6 +61,10 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
@@ -70,13 +86,17 @@ export default {
|
||||
default: -1,
|
||||
},
|
||||
iclass: {
|
||||
type: String,
|
||||
type: [String, Array],
|
||||
default: ''
|
||||
},
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
validator: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
@@ -84,17 +104,72 @@ export default {
|
||||
return this.id + this._uid;
|
||||
},
|
||||
inputClass() {
|
||||
return this.iclass != '' ? this.iclass : 'br2 f5 w-100 ba b--black-40 pa2 outline-0';
|
||||
var c = [this.iclass != '' ? this.iclass : 'br2 f5 w-100 ba b--black-40 pa2 outline-0'];
|
||||
if (this.validator) {
|
||||
c.push({ error: this.validator.$error });
|
||||
}
|
||||
c.push('input');
|
||||
return c;
|
||||
},
|
||||
inputStyle() {
|
||||
return this.width >= 0 ? 'width:' + this.width + 'px' : '';
|
||||
}
|
||||
},
|
||||
|
||||
field() {
|
||||
return this.label && this.label.length > 0 ? this.label : this.title;
|
||||
},
|
||||
requiredMessage() {
|
||||
return this.$t('validation.vue.required', { field: this.field });
|
||||
},
|
||||
urlMessage() {
|
||||
return this.$t('validation.vue.url', { field: this.field });
|
||||
},
|
||||
maxLengthMessage() {
|
||||
var type = 'string';
|
||||
switch (this.inputType) {
|
||||
case 'number':
|
||||
type = 'numeric';
|
||||
break;
|
||||
}
|
||||
return this.$t('validation.vue.max.'.type, {
|
||||
field: this.field,
|
||||
max: this.validator ? this.validator.$params.maxLength.max : '',
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
focus() {
|
||||
this.$refs.input.focus();
|
||||
},
|
||||
|
||||
onInput(event) {
|
||||
if (this.validator && event.data !== undefined) {
|
||||
this.validator.$reset();
|
||||
}
|
||||
this.$emit('input', event.target.value);
|
||||
},
|
||||
|
||||
onSubmit(event) {
|
||||
if (this.validator) {
|
||||
this.validator.$touch();
|
||||
}
|
||||
this.$emit('submit', event.target.value);
|
||||
},
|
||||
|
||||
onBlur(event) {
|
||||
if (this.validator && event.target.value !== '') {
|
||||
this.validator.$touch();
|
||||
}
|
||||
this.$emit('blur', event.target.value);
|
||||
},
|
||||
|
||||
onChange(event) {
|
||||
if (this.validator) {
|
||||
this.validator.$touch();
|
||||
}
|
||||
this.$emit('change', event.target.value);
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div>
|
||||
<p-input
|
||||
ref="input"
|
||||
v-model.lazy="modelValue"
|
||||
v-model.lazy="prop"
|
||||
:name="name"
|
||||
:type="_type"
|
||||
:class="inputClass"
|
||||
@@ -14,7 +14,7 @@
|
||||
:value="value"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
@change="event => { $emit('change', event) }"
|
||||
@change="$emit('change', $event)"
|
||||
>
|
||||
<slot></slot>
|
||||
<slot slot="extra" name="inputextra"></slot>
|
||||
@@ -82,6 +82,12 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
prop: null
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
_type() {
|
||||
if (this.$options.input_type) {
|
||||
@@ -97,6 +103,16 @@ export default {
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
modelValue(val) {
|
||||
this.prop = val;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prop = this.modelValue;
|
||||
},
|
||||
|
||||
methods: {
|
||||
select() {
|
||||
if (this.disabled) {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<style scoped>
|
||||
select {
|
||||
.select {
|
||||
height: 34px;
|
||||
transition: all;
|
||||
transition-duration: 0.2s;
|
||||
border: 1px solid #c4cdd5;
|
||||
}
|
||||
select:focus {
|
||||
.select:focus {
|
||||
border: 1px solid #5c6ac4;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div :class="{ 'form-group-error': validator && validator.$error }">
|
||||
<label
|
||||
v-if="title"
|
||||
:for="realid"
|
||||
class="mb2"
|
||||
:class="{ b: required }"
|
||||
:class="{ b: required, error: validator && validator.$error }"
|
||||
>
|
||||
{{ title }}
|
||||
</label>
|
||||
@@ -27,7 +27,7 @@ select:focus {
|
||||
:name="id"
|
||||
:required="required"
|
||||
:class="selectClass"
|
||||
@input="event => { $emit('input', event.target.value) }"
|
||||
@input="onInput"
|
||||
>
|
||||
<template v-if="Array.isArray(options)">
|
||||
<option
|
||||
@@ -54,6 +54,9 @@ select:focus {
|
||||
</optgroup>
|
||||
</template>
|
||||
</select>
|
||||
<small v-if="validator && (validator.$error && validator.required !== undefined && !validator.required)" class="error">
|
||||
{{ requiredMessage }}
|
||||
</small>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -73,6 +76,10 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
@@ -89,6 +96,10 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
validator: {
|
||||
type: Object,
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
@@ -102,8 +113,19 @@ export default {
|
||||
return this.id + this._uid;
|
||||
},
|
||||
selectClass() {
|
||||
return this.iclass != '' ? this.iclass : 'br2 f5 w-100 ba b--black-40 pa2 outline-0';
|
||||
}
|
||||
var c = [this.iclass != '' ? this.iclass : 'br2 f5 w-100 ba b--black-40 pa2 outline-0'];
|
||||
if (this.validator) {
|
||||
c.push({ error: this.validator.$error });
|
||||
}
|
||||
c.push('select');
|
||||
return c;
|
||||
},
|
||||
field() {
|
||||
return this.label && this.label.length > 0 ? this.label : this.title;
|
||||
},
|
||||
requiredMessage() {
|
||||
return this.$t('validation.vue.required', { field: this.field });
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -129,7 +151,14 @@ export default {
|
||||
|
||||
focus() {
|
||||
this.$refs.select.focus();
|
||||
}
|
||||
},
|
||||
|
||||
onInput(event) {
|
||||
if (this.validator) {
|
||||
this.validator.$touch();
|
||||
}
|
||||
this.$emit('input', event.target.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -80,24 +80,26 @@
|
||||
</div>
|
||||
|
||||
<!-- Create Client Modal -->
|
||||
<sweet-modal ref="modalCreateClient" overlay-theme="dark" tabindex="-1" role="dialog"
|
||||
:title="$t('settings.api_oauth_create')" @open="_focusCreateInput"
|
||||
<sweet-modal ref="modalClient" overlay-theme="dark" tabindex="-1" role="dialog"
|
||||
:title="form.id ? $t('settings.api_oauth_edit') : $t('settings.api_oauth_create')"
|
||||
@open="_focusInput"
|
||||
>
|
||||
<!-- Form Errors -->
|
||||
<error :errors="createForm.errors" />
|
||||
<error :errors="form.errors" />
|
||||
|
||||
<!-- Create Client Form -->
|
||||
<form class="form-horizontal" role="form">
|
||||
<form ref="form" class="form-horizontal" role="form">
|
||||
<!-- Name -->
|
||||
<div class="form-group">
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
:id="'create-client-name'"
|
||||
ref="createClientName"
|
||||
v-model="createForm.name"
|
||||
:id="'client-name'"
|
||||
ref="clientName"
|
||||
v-model="form.name"
|
||||
:iclass="'br2 f5 w-50 ba b--black-40 pa2 outline-0'"
|
||||
:required="true"
|
||||
:title="$t('settings.api_oauth_name')"
|
||||
:validator="$v.form.name"
|
||||
@submit="store"
|
||||
/>
|
||||
|
||||
@@ -111,11 +113,12 @@
|
||||
<div class="form-group">
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
:id="'create-redirect-url'"
|
||||
v-model="createForm.redirect"
|
||||
:id="'redirect-url'"
|
||||
v-model="form.redirect"
|
||||
:iclass="'br2 f5 w-50 ba b--black-40 pa2 outline-0'"
|
||||
:required="true"
|
||||
:title="$t('settings.api_oauth_redirecturl')"
|
||||
:validator="$v.form.redirect"
|
||||
@submit="store"
|
||||
/>
|
||||
|
||||
@@ -132,65 +135,7 @@
|
||||
{{ $t('app.close') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="store">
|
||||
{{ $t('app.create') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
|
||||
<!-- Edit Client Modal -->
|
||||
<sweet-modal ref="modalEditClient" overlay-theme="dark" tabindex="-1" role="dialog"
|
||||
:title="$t('settings.api_oauth_edit')" @open="_focusEditInput"
|
||||
>
|
||||
<!-- Form Errors -->
|
||||
<error :errors="editForm.errors" />
|
||||
|
||||
<!-- Edit Client Form -->
|
||||
<form class="form-horizontal" role="form">
|
||||
<!-- Name -->
|
||||
<div class="form-group">
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
:id="'edit-client-name'"
|
||||
ref="editClientName"
|
||||
v-model="editForm.name"
|
||||
:iclass="'br2 f5 w-50 ba b--black-40 pa2 outline-0'"
|
||||
:required="true"
|
||||
:title="$t('settings.api_oauth_name')"
|
||||
@submit="update"
|
||||
/>
|
||||
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.api_oauth_name_help') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Redirect URL -->
|
||||
<div class="form-group">
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
:id="'edit-redirect-url'"
|
||||
v-model="editForm.redirect"
|
||||
:iclass="'br2 f5 w-50 ba b--black-40 pa2 outline-0'"
|
||||
:required="true"
|
||||
:title="$t('settings.api_oauth_redirecturl')"
|
||||
@submit="update"
|
||||
/>
|
||||
|
||||
<small class="form-text text-muted">
|
||||
{{ $t('settings.api_oauth_redirecturl_help') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Modal Actions -->
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeModal">
|
||||
{{ $t('app.close') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" href="" @click.prevent="update">
|
||||
{{ $t('app.save') }}
|
||||
{{ form.id ? $t('app.save') : $t('app.create') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
@@ -200,6 +145,8 @@
|
||||
<script>
|
||||
import Error from '../partials/Error.vue';
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
import { validationMixin } from 'vuelidate';
|
||||
import { required, url } from 'vuelidate/lib/validators';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -208,17 +155,13 @@ export default {
|
||||
Error
|
||||
},
|
||||
|
||||
mixins: [validationMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
clients: [],
|
||||
|
||||
createForm: {
|
||||
errors: [],
|
||||
name: '',
|
||||
redirect: ''
|
||||
},
|
||||
|
||||
editForm: {
|
||||
form: {
|
||||
errors: [],
|
||||
name: '',
|
||||
redirect: ''
|
||||
@@ -226,6 +169,18 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
validations: {
|
||||
form: {
|
||||
name: {
|
||||
required,
|
||||
},
|
||||
redirect: {
|
||||
required,
|
||||
url,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir == 'ltr';
|
||||
@@ -244,26 +199,16 @@ export default {
|
||||
/**
|
||||
* Focus on modal open.
|
||||
*/
|
||||
_focusCreateInput() {
|
||||
_focusInput() {
|
||||
let vm = this;
|
||||
setTimeout(function() {
|
||||
vm.$refs.createClientName.focus();
|
||||
vm.$refs.clientName.focus();
|
||||
}, 10);
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus on modal open.
|
||||
* Get all of the OAuth clients for the user.
|
||||
*/
|
||||
_focusEditInput() {
|
||||
let vm = this;
|
||||
setTimeout(function() {
|
||||
vm.$refs.editClientName.focus();
|
||||
}, 10);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all of the OAuth clients for the user.
|
||||
*/
|
||||
getClients() {
|
||||
axios.get('oauth/clients')
|
||||
.then(response => {
|
||||
@@ -272,46 +217,41 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the form for creating new clients.
|
||||
*/
|
||||
* Show the form for creating new clients.
|
||||
*/
|
||||
showCreateClientForm() {
|
||||
this.$refs.modalCreateClient.open();
|
||||
this.resetField();
|
||||
this.$refs.modalClient.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new OAuth client for the user.
|
||||
*/
|
||||
* Create a new OAuth client for the user.
|
||||
*/
|
||||
store() {
|
||||
this.persistClient(
|
||||
'post', 'oauth/clients',
|
||||
this.createForm
|
||||
);
|
||||
this.$v.$touch();
|
||||
|
||||
if (this.$v.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
let method = this.form.id ? 'put' : 'post';
|
||||
let url = this.form.id ? 'oauth/clients/' + this.form.id : 'oauth/clients';
|
||||
|
||||
this.persistClient(method, url, this.form);
|
||||
},
|
||||
|
||||
/**
|
||||
* Edit the given client.
|
||||
*/
|
||||
* Edit the given client.
|
||||
*/
|
||||
edit(client) {
|
||||
this.editForm.id = client.id;
|
||||
this.editForm.name = client.name;
|
||||
this.editForm.redirect = client.redirect;
|
||||
this.form = Object.assign({errors:[]}, client);
|
||||
|
||||
this.$refs.modalEditClient.open();
|
||||
this.$refs.modalClient.open();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the client being edited.
|
||||
*/
|
||||
update() {
|
||||
this.persistClient(
|
||||
'put', 'oauth/clients/' + this.editForm.id,
|
||||
this.editForm
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Persist the client to storage using the given form.
|
||||
*/
|
||||
* Persist the client to storage using the given form.
|
||||
*/
|
||||
persistClient(method, uri, form) {
|
||||
form.errors = [];
|
||||
|
||||
@@ -319,10 +259,6 @@ export default {
|
||||
.then(response => {
|
||||
this.getClients();
|
||||
|
||||
form.name = '';
|
||||
form.redirect = '';
|
||||
form.errors = [];
|
||||
|
||||
this.closeModal();
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -335,8 +271,8 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the given client.
|
||||
*/
|
||||
* Destroy the given client.
|
||||
*/
|
||||
destroy(client) {
|
||||
axios.delete('oauth/clients/' + client.id)
|
||||
.then(response => {
|
||||
@@ -345,8 +281,19 @@ export default {
|
||||
},
|
||||
|
||||
closeModal() {
|
||||
this.$refs.modalCreateClient.close();
|
||||
this.$refs.modalEditClient.close();
|
||||
this.resetField();
|
||||
this.$v.$reset();
|
||||
this.$refs.form.reset();
|
||||
this.$refs.modalClient.close();
|
||||
},
|
||||
|
||||
resetField() {
|
||||
this.form = {
|
||||
id: '',
|
||||
errors:[],
|
||||
name: '',
|
||||
redirect: '',
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<error :errors="form.errors" />
|
||||
|
||||
<!-- Create Token Form -->
|
||||
<form class="form-horizontal" role="form" @submit.prevent="store">
|
||||
<form ref="form" class="form-horizontal" role="form" @submit.prevent="store">
|
||||
<!-- Name -->
|
||||
<div class="col-md-auto">
|
||||
<form-input
|
||||
@@ -80,6 +80,7 @@
|
||||
:iclass="'br2 f5 w-50 ba b--black-40 pa2 outline-0'"
|
||||
:required="true"
|
||||
:title="$t('settings.api_token_name')"
|
||||
:validator="$v.form.name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -141,6 +142,8 @@
|
||||
<script>
|
||||
import Error from '../partials/Error.vue';
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
import { validationMixin } from 'vuelidate';
|
||||
import { required } from 'vuelidate/lib/validators';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -149,6 +152,8 @@ export default {
|
||||
Error
|
||||
},
|
||||
|
||||
mixins: [validationMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
endpoint: '',
|
||||
@@ -165,6 +170,14 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
validations: {
|
||||
form: {
|
||||
name: {
|
||||
required,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir == 'ltr';
|
||||
@@ -207,6 +220,8 @@ export default {
|
||||
* Close all modals.
|
||||
*/
|
||||
closeModal() {
|
||||
this.$refs.form.reset();
|
||||
this.$v.$reset();
|
||||
this.$refs.modalCreateToken.close();
|
||||
this.$refs.modalAccessToken.close();
|
||||
},
|
||||
@@ -232,6 +247,12 @@ export default {
|
||||
* Create a new personal access token.
|
||||
*/
|
||||
store() {
|
||||
this.$v.$touch();
|
||||
|
||||
if (this.$v.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.accessToken = null;
|
||||
|
||||
this.form.errors = [];
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
<style scoped>
|
||||
.error {
|
||||
animation-name: shakeError;
|
||||
animation-fill-mode: forwards;
|
||||
animation-duration: .6s;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.form-group-error {
|
||||
display: block;
|
||||
color:#f57f6c;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
@@ -113,16 +124,19 @@
|
||||
<div class="mb2">
|
||||
<toggle-button class="mr2" :sync="true" :labels="true" :value="stateInput" @change="stateInput = !stateInput" />
|
||||
<div class="dib relative" style="top: -2px;">
|
||||
<stay-in-touch-label v-model="frequencyInput">
|
||||
<stay-in-touch-label
|
||||
v-model="frequencyInput"
|
||||
:class="{ 'form-group-error': $v.frequencyInput.$error }"
|
||||
>
|
||||
<div class="dib">
|
||||
<form-input
|
||||
:id="'frequency'"
|
||||
v-model="frequencyInput"
|
||||
:value="frequencyInput"
|
||||
v-model.number="frequencyInput"
|
||||
:input-type="'number'"
|
||||
:width="60"
|
||||
:required="true"
|
||||
@input="stateInput = $event > 0"
|
||||
:validator="$v.frequencyInput"
|
||||
@input="onInput($event)"
|
||||
/>
|
||||
</div>
|
||||
</stay-in-touch-label>
|
||||
@@ -153,6 +167,8 @@
|
||||
<script>
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
import { ToggleButton } from 'vue-js-toggle-button';
|
||||
import { validationMixin } from 'vuelidate';
|
||||
import { required, numeric } from 'vuelidate/lib/validators';
|
||||
|
||||
let StayInTouchLabel = Vue.component('stay-in-touch-label', {
|
||||
|
||||
@@ -181,6 +197,8 @@ export default {
|
||||
StayInTouchLabel,
|
||||
},
|
||||
|
||||
mixins: [validationMixin],
|
||||
|
||||
props: {
|
||||
hash: {
|
||||
type: String,
|
||||
@@ -196,6 +214,13 @@ export default {
|
||||
},
|
||||
},
|
||||
|
||||
validations: {
|
||||
frequencyInput: {
|
||||
required,
|
||||
numeric,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
frequency: 0,
|
||||
@@ -250,10 +275,9 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure we can't press update if the frequency is invalid
|
||||
// and if the feature is activated
|
||||
if ((this.frequencyInput == '' || this.frequencyInput < 1) && this.stateInput) {
|
||||
this.errorMessage = this.$t('people.stay_in_touch_invalid');
|
||||
this.$v.$touch();
|
||||
|
||||
if (this.$v.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<style scoped>
|
||||
.photo {
|
||||
height: 200px;
|
||||
}
|
||||
.photo {
|
||||
height: 200px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
@@ -58,6 +58,7 @@
|
||||
:required="true"
|
||||
:class="'dtc pr2'"
|
||||
:title="$t('people.gifts_add_gift_name')"
|
||||
:validator="$v.newGift.name"
|
||||
@submit="store"
|
||||
/>
|
||||
</div>
|
||||
@@ -126,14 +127,16 @@
|
||||
<form-checkbox
|
||||
v-model="hasRecipient"
|
||||
:name="'has_recipient'"
|
||||
@change="() => { this.$refs.recipient.focus() }"
|
||||
@change="(val) => { if (val) { $refs.recipient.focus() } }"
|
||||
>
|
||||
{{ $t('people.gifts_add_someone', {name: ''}) }}
|
||||
</form-checkbox>
|
||||
<form-select
|
||||
ref="recipient"
|
||||
v-model="newGift.recipient_id"
|
||||
:label="$t('people.gifts_add_recipient_field')"
|
||||
:options="familyContacts"
|
||||
:validator="$v.newGift.recipient_id"
|
||||
@input="hasRecipient = true"
|
||||
/>
|
||||
</div>
|
||||
@@ -205,6 +208,8 @@
|
||||
|
||||
import Error from '../../partials/Error.vue';
|
||||
import PhotoUpload from '../photo/PhotoUpload.vue';
|
||||
import { validationMixin } from 'vuelidate';
|
||||
import { required, maxLength } from 'vuelidate/lib/validators';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -212,6 +217,8 @@ export default {
|
||||
PhotoUpload
|
||||
},
|
||||
|
||||
mixins: [validationMixin],
|
||||
|
||||
props: {
|
||||
contactId: {
|
||||
type: Number,
|
||||
@@ -254,6 +261,27 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
validations() {
|
||||
var v = {
|
||||
newGift: {
|
||||
name: {
|
||||
required,
|
||||
maxLength: maxLength(255),
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if (this.hasRecipient) {
|
||||
v.newGift = Object.assign(v.newGift, {
|
||||
recipient_id: {
|
||||
required,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return v;
|
||||
},
|
||||
|
||||
computed: {
|
||||
locale() {
|
||||
return this.$root.locale;
|
||||
@@ -307,6 +335,7 @@ export default {
|
||||
this.displayUpload= this.gift ? this.gift.photos.length > 0 : false;
|
||||
|
||||
this.errors = [];
|
||||
this.$v.$reset();
|
||||
},
|
||||
|
||||
close() {
|
||||
@@ -319,6 +348,12 @@ export default {
|
||||
this.newGift.recipient_id = null;
|
||||
}
|
||||
|
||||
this.$v.$touch();
|
||||
|
||||
if (this.$v.$invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
let method = this.gift ? 'put' : 'post';
|
||||
let url = this.gift ? 'api/gifts/'+this.gift.id : 'api/gifts';
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ return [
|
||||
'information_edit_probably' => 'This person is probably...',
|
||||
'information_edit_not_year' => 'I know the day and month of the birthdate of this person, but not the year…',
|
||||
'information_edit_exact' => 'I know the exact birthdate of this person...',
|
||||
'information_edit_birthdate_label' => 'Birthdate',
|
||||
'information_no_work_defined' => 'No work information defined',
|
||||
'information_work_at' => 'at :company',
|
||||
'work_add_cta' => 'Update work information',
|
||||
@@ -308,6 +309,7 @@ return [
|
||||
'gifts_add_value' => 'Value (optional)',
|
||||
'gifts_add_comment' => 'Comment (optional)',
|
||||
'gifts_add_recipient' => 'Recipient (optional)',
|
||||
'gifts_add_recipient_field' => 'Recipient',
|
||||
'gifts_add_photo' => 'Photo (optional)',
|
||||
'gifts_add_photo_title' => 'Add a photo for this gift',
|
||||
'gifts_add_someone' => 'This gift is for someone in {name}’s family in particular',
|
||||
@@ -366,6 +368,7 @@ return [
|
||||
'deceased_know_date' => 'I know the date this person died',
|
||||
'deceased_add_reminder' => 'Add a reminder for this date',
|
||||
'deceased_label' => 'Deceased',
|
||||
'deceased_date_label' => 'Deceased date',
|
||||
'deceased_label_with_date' => 'Deceased on :date',
|
||||
'deceased_age' => 'Age at death',
|
||||
|
||||
|
||||
@@ -120,4 +120,12 @@ return [
|
||||
|
||||
'attributes' => [],
|
||||
|
||||
'vue' => [
|
||||
'max' => [
|
||||
'numeric' => '{field} may not be greater than {max}.',
|
||||
'string' => '{field} may not be greater than {max} characters.',
|
||||
],
|
||||
'required' => '{field} is required.',
|
||||
'url' => '{field} is not a valid URL.',
|
||||
],
|
||||
];
|
||||
|
||||
Vendored
+29
@@ -594,3 +594,32 @@ footer {
|
||||
svg {
|
||||
vertical-align: baseline !important;
|
||||
}
|
||||
|
||||
// Input error handle
|
||||
.form-group-error {
|
||||
animation-name: shake;
|
||||
animation-fill-mode: forwards;
|
||||
animation-duration: .6s;
|
||||
animation-timing-function: ease-in-out;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.form-group-error .error {
|
||||
border-color: #f79483 !important;
|
||||
color:#f57f6c;
|
||||
}
|
||||
|
||||
// Shake animation
|
||||
@keyframes shake {
|
||||
0%, 100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
15%, 45%, 75% {
|
||||
transform: translateX(0.375rem);
|
||||
}
|
||||
|
||||
30%, 60%, 90% {
|
||||
transform: translateX(-0.375rem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9673,6 +9673,11 @@ vue@^2.5.21:
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
|
||||
integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==
|
||||
|
||||
vuelidate@^0.7.4:
|
||||
version "0.7.4"
|
||||
resolved "https://registry.yarnpkg.com/vuelidate/-/vuelidate-0.7.4.tgz#5a0e54be09ac0192f1aa3387d74b92e0945bf8aa"
|
||||
integrity sha512-QHZWYOL325Zo+2K7VBNEJTZ496Kd8Z31p85aQJFldKudUUGBmgw4zu4ghl4CyqPwjRCmqZ9lDdx4FSdMnu4fGg==
|
||||
|
||||
watchpack@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
|
||||
|
||||
Reference in New Issue
Block a user