refactor: replace custom CRM with Monica fork
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s

This commit is contained in:
Kroonk
2026-05-22 16:19:55 +02:00
parent 38cc4c09ca
commit a213a1dba0
2215 changed files with 242567 additions and 6015 deletions

View File

@@ -0,0 +1,58 @@
<style scoped>
.avatar-padding {
padding-top: 19%
}
</style>
<template>
<span>
<img v-if="check"
v-tooltip.bottom="contact.complete_name"
:class="['br4 h3 w3 dib tc', imgclass]"
:alt="contact.initials"
:src="avatar_url"
@error="check=false"
/>
<span v-else
v-tooltip.bottom="contact.complete_name"
:class="['br4 h3 w3 dib tc', 'white f3 avatar-padding', imgclass]"
:style="'background-color: '+ default_avatar_color"
>
{{ contact.initials }}
</span>
</span>
</template>
<script>
export default {
props: {
contact: {
type: Object,
default: null,
},
imgclass: {
type: String,
default: '',
},
},
data () {
return {
check: true,
};
},
computed: {
avatar_url() {
if (_.isObject(this.contact.information)) {
return this.contact.information.avatar.url;
}
return this.contact.avatar_url;
},
default_avatar_color() {
if (_.isObject(this.contact.information)) {
return this.contact.information.avatar.default_avatar_color;
}
return this.contact.default_avatar_color;
}
}
};
</script>

View File

@@ -0,0 +1,38 @@
<template>
<div class="avatars">
<a v-if="clickable === true" :href="'people/' + id">
<avatarimg :contact="contact" :imgclass="imgclass" />
</a>
<avatarimg v-else :contact="contact" :imgclass="imgclass" />
</div>
</template>
<script>
import Avatarimg from './Avatar.img.vue';
export default {
components: {
Avatarimg
},
props: {
contact: {
type: Object,
default: null,
},
clickable: {
type: Boolean,
default: true,
},
imgclass: {
type: String,
default: '',
},
},
computed: {
id() {
return this.contact.hash_id ? this.contact.hash_id : this.contact.id;
},
}
};
</script>

View File

@@ -0,0 +1,74 @@
<template>
<div class="di">
<a :class="lclass" :title="title" href="" @click.prevent="open">
<slot>
</slot>
</a>
<sweet-modal ref="modal" tabindex="-1" role="dialog">
<div>
{{ message }}
</div>
<!-- Modal Actions -->
<div slot="button" class="flex-ns justify-between">
<a class="btn mt2" href="" @click.prevent="close">
{{ $t('app.cancel') }}
</a>
<button v-cy-name="'confirm-' + name" class="btn btn-primary w-auto-ns w100 mt2 pb0-ns" @click="confirm($event)">
{{ $t('app.confirm') }}
</button>
</div>
</sweet-modal>
</div>
</template>
<script>
import { SweetModal } from 'sweet-modal-vue';
export default {
components: {
SweetModal,
},
props: {
name: {
type: String,
default: '',
},
title: {
type: String,
default: '',
},
message: {
type: String,
default: '',
},
linkClass: {
type: [String, Array],
default: '',
},
},
computed: {
lclass() {
return this.linkClass === '' ? 'pointer' : this.linkClass;
}
},
methods: {
open() {
this.$refs.modal.open();
},
close() {
this.$refs.modal.close();
},
confirm(event) {
this.close();
this.$emit('confirm', event);
}
}
};
</script>

View File

@@ -0,0 +1,51 @@
<template>
<div v-if="apierror || errors.length > 0" class="alert alert-danger">
<p>{{ $t('app.error_title') }}</p>
<template v-if="apierror">
<ul v-if="apimessage">
<li v-for="error in errors[0].message" :key="error.id">
{{ error }}
</li>
</ul>
<p v-else>
{{ errors[0].message }}
</p>
</template>
<template v-else>
<p v-if="errors[0] !== 'The given data was invalid.'">
{{ errors[0] }}
</p>
<template v-if="display(errors[1])">
<ul v-for="errorsList in errors[1]" :key="errorsList.id">
<li v-for="error in errorsList" :key="error.id">
{{ error }}
</li>
</ul>
</template>
</template>
</div>
</template>
<script>
export default {
props: {
errors: {
type: [Array, Object],
default: () => [],
},
},
computed: {
apierror() {
return _.isObject(this.errors[0]) && this.errors[0].error_code !== undefined;
},
apimessage() {
return _.isArray(this.errors[0].message);
}
},
methods: {
display($val) {
return _.isObject($val);
},
}
};
</script>

View File

@@ -0,0 +1,255 @@
<style scoped>
</style>
<template>
<div>
<div class="pa4-ns ph3 pv2 bb b--gray-monica">
<div class="mb3 mb0-ns">
<form-radio
v-model.lazy="selectedOption"
:name="'birthdate'"
:value="'unknown'"
:dclass="'flex mb3'"
:iclass="[ dirltr ? 'mr2' : 'ml2' ]"
@change="event => { _saveOption(); }"
>
<template slot="label">
{{ $t('people.information_edit_unknown') }}
</template>
</form-radio>
<form-radio
v-model.lazy="selectedOption"
:name="'birthdate'"
:value="'approximate'"
:dclass="'flex mb3'"
:iclass="[ dirltr ? 'mr2' : 'ml2' ]"
@change="event => { if (selectedOptionSave !== 'approximate') {_focusAge();} _saveOption(); }"
>
<template slot="label">
{{ $t('people.information_edit_probably') }}
</template>
<div v-if="selectedOption === 'approximate'" slot="extra">
<form-input
:id="'age'"
ref="age"
v-model="selectedAge"
:input-type="'number'"
:width="50"
:required="true"
:validator="$v.selectedAge"
/>
</div>
</form-radio>
<form-radio
v-model.lazy="selectedOption"
:name="'birthdate'"
:value="'almost'"
:dclass="'flex mb3'"
:iclass="[ dirltr ? 'mr2' : 'ml2' ]"
@change="event => { if (selectedOptionSave !== 'almost') {_focusMonth();} _saveOption(); }"
>
<template slot="label">
{{ $t('people.information_edit_not_year') }}
</template>
<div v-if="selectedOption === 'almost'" slot="extra" class="mt2">
<div class="flex">
<form-select
:id="'month'"
ref="month"
v-model="selectedMonth"
:options="months"
:title="''"
:class="[ dirltr ? 'mr3' : '' ]"
/>
<form-select
:id="'day'"
v-model="selectedDay"
:options="days"
:title="''"
:class="[ dirltr ? '' : 'mr3' ]"
/>
</div>
</div>
</form-radio>
<form-radio
v-model.lazy="selectedOption"
:name="'birthdate'"
:value="'exact'"
:dclass="'flex mb3'"
:iclass="[ dirltr ? 'mr2' : 'ml2' ]"
@change="event => { if (selectedOptionSave !== 'exact') {_focusBirthday();} _saveOption(); }"
>
<template slot="label">
{{ $t('people.information_edit_exact') }}
</template>
<div v-if="selectedOption === 'exact'" slot="extra" class="mt2">
<form-date
:id="'birthdayDate'"
ref="birthday"
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>
</div>
</div>
<div v-if="selectedOption === 'exact' || selectedOption === 'almost'" class="pa4-ns ph3 pv2 bb b--gray-monica">
<div class="mb2 mb0-ns">
<form-checkbox
v-model.lazy="hasBirthdayReminder"
:name="'addReminder'"
:value="'addReminder'"
:dclass="[ 'flex', dirltr ? 'mr2' : 'ml2' ]"
>
<template slot="label">
{{ $t('people.people_add_reminder_for_birthday') }}
</template>
</form-checkbox>
</div>
</div>
</div>
</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,
default: '',
},
days: {
type: Array,
default: () => [],
},
months: {
type: Array,
default: () => [],
},
day: {
type: Number,
default: 0,
},
month: {
type: Number,
default: 0,
},
birthdate: {
type: String,
default: '',
},
age: {
type: Number,
default: 0,
},
reminder: {
type: Boolean,
default: false,
},
},
data() {
return {
selectedDate: null,
selectedOption: null,
selectedOptionSave: null,
selectedAge: 0,
selectedMonth: 0,
selectedDay: 0,
hasBirthdayReminder: false
};
},
validations() {
switch (this.selectedOption) {
case 'approximate':
return {
selectedAge: {
required,
numeric,
}
};
case 'exact':
return {
selectedDate: {
required,
before: before(moment())
}
};
}
return null;
},
computed: {
dirltr() {
return this.$root.htmldir === 'ltr';
},
locale() {
return this.$root.locale;
}
},
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;
},
methods: {
_focusAge() {
setTimeout(() => {
this.$refs.age.focus();
}, 100);
},
_focusMonth() {
setTimeout(() => {
this.$refs.month.focus();
}, 100);
},
_focusBirthday() {
setTimeout(() => {
this.$refs.birthday.focus();
}, 100);
},
_saveOption() {
this.selectedOptionSave = this.selectedOption;
},
}
};
</script>

View File

@@ -0,0 +1,133 @@
<style scoped>
</style>
<template>
<div class="pa4-ns ph3 pv2 bb b--gray-monica">
<div class="mb3 mb0-ns">
<form-checkbox
v-model.lazy="deceased"
:name="'is_deceased'"
:value="true"
:dclass="'flex mb2'"
>
<template slot="label">
{{ $t('people.deceased_mark_person_deceased') }}
</template>
</form-checkbox>
<div v-show="deceased" :class="[ dirltr ? 'ml4' : 'mr4' ]">
<form-checkbox
v-model.lazy="dateKnown"
:name="'is_deceased_date_known'"
:value="true"
:dclass="'flex mb1'"
@change="_focusDate()"
>
<template slot="label">
{{ $t('people.deceased_know_date') }}
</template>
</form-checkbox>
<div v-show="dateKnown" :class="[ dirltr ? 'ml4' : 'mr4' ]">
<form-date
:id="'deceased_date'"
ref="deaceasedday"
v-model="selectedDate"
:label="$t('people.deceased_date_label')"
:show-calendar-on-focus="true"
:locale="locale"
:validator="$v.selectedDate"
/>
<div v-show="selectedDate !== ''" class="mt2">
<form-checkbox
:name="'add_reminder_deceased'"
:value="true"
:model="reminder"
>
{{ $t('people.deceased_add_reminder') }}
</form-checkbox>
</div>
</div>
</div>
</div>
</div>
</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,
default: false,
},
date: {
type: String,
default: '',
},
reminder: {
type: Boolean,
default: false,
},
},
data() {
return {
deceased: false,
dateKnown: false,
selectedDate: null,
};
},
validations: {
selectedDate: {
required,
before: before(moment())
}
},
computed: {
dirltr() {
return this.$root.htmldir === 'ltr';
},
locale() {
return this.$root.locale;
}
},
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: {
_focusDate() {
setTimeout(() => {
this.$refs.deaceasedday.focus();
}, 100);
},
}
};
</script>

View File

@@ -0,0 +1,23 @@
<script>
const input = require('./PInput').default;
const checkbox = {
name: 'checkbox',
input_type: 'checkbox',
input_iclass: 'p-curve p-thick',
components: input.components,
model: input.model,
props: input.props,
data: input.data,
computed: input.computed,
watch: input.watch,
mounted: input.mounted,
methods: input.methods,
render: input.render,
};
export default checkbox;
</script>

View File

@@ -0,0 +1,206 @@
<template>
<div :class="{ 'form-group-error': validator && validator.$error }">
<datepicker
ref="select"
:ref-name="'select'"
:value="selectedDate"
:format="displayValue"
:parse-typed-date="formatTypedValue"
:language="locale"
:monday-first="mondayFirst"
:input-class="inputClass"
:typeable="true"
:clear-button="true"
:show-calendar-on-focus="showCalendarOnFocus"
@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>
<script>
import Datepicker from '@hokify/vuejs-datepicker';
import moment from 'moment';
export default {
components: {
Datepicker
},
model: {
prop: 'value',
event: 'input'
},
props: {
id: {
type: String,
default: '',
},
value: {
type: String,
default: '',
},
label: {
type: String,
default: '',
},
defaultDate: {
type: String,
default: '',
},
locale: {
type: String,
default: '',
},
showCalendarOnFocus: {
type: Boolean,
default: false,
},
validator: {
type: Object,
default: null,
},
},
data() {
return {
/**
* Value of the date in exchange format
*/
exchange: '',
selectedDate: '',
mondayFirst: false
};
},
computed: {
/**
* Exchange format with controller (moment format type).
*/
exchangeFormat() {
return 'YYYY-MM-DD';
},
/**
* Display format (moment format type).
*/
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: {
value: function (newValue) {
this.updateExchange(newValue);
}
},
mounted() {
this.updateExchange(this.value === '' ? this.defaultDate : this.value);
this.mondayFirst = moment.localeData().firstDayOfWeek() === 1;
},
methods: {
/**
* Format date for display it.
* @param date string in locale format
* @return string date in display format
*/
displayValue(date) {
return date !== '' && date !== null ? moment(date).format(this.displayFormat) : '';
},
/**
* Format date for save it.
* @param date string in locale format
* @return string date in exchange format
*/
exchangeValue(date) {
return date !== '' && date !== null ? moment(date).format(this.exchangeFormat) : '';
},
/**
* Update the value of hidden input.
* Store it in exchange format value.
*/
update(date) {
if (date === '' || date === null) {
this.exchange = '';
} else {
var mdate = moment(date);
if (! mdate.isValid()) {
mdate = moment();
}
this.exchange = mdate.format(this.exchangeFormat);
}
},
updateExchange(date) {
this.exchange = date;
if (this.exchange !== '') {
var mdate = moment(this.exchange, this.exchangeFormat);
if (! mdate.isValid()) {
mdate = moment();
}
this.selectedDate = mdate.toDate();
}
this.update(this.selectedDate);
},
/**
* Format the typed value with the locale specification.
* @param date string in locale format
* @return date value
*/
formatTypedValue(date) {
return date !== '' && date !== null ? moment(date, this.displayFormat).toDate() : '';
},
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>

View File

@@ -0,0 +1,181 @@
<style scoped>
.input {
transition: all;
transition-duration: 0.2s;
border: 1px solid #c4cdd5;
}
.input:focus {
border: 1px solid #5c6ac4;
}
</style>
<template>
<div :class="{ 'form-group-error': validator && validator.$error }">
<label
v-if="title"
:for="realid"
class="mb2"
:class="{ b: required, error: validator && validator.$error }"
>
{{ title }}
</label>
<input
:id="realid"
ref="input"
:type="inputType"
autofocus
:required="required"
:name="id"
:placeholder="placeholder"
:class="inputClass"
:style="inputStyle"
:value="value"
:maxlength="maxlength"
:step="step"
@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>
<script>
export default {
props: {
value: {
type: [String, Number],
default: '',
},
title: {
type: String,
default: '',
},
label: {
type: String,
default: null,
},
id: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
},
required: {
type: Boolean,
default: true,
},
inputType: {
type: String,
default: '',
},
step: {
type: String,
default: '',
},
width: {
type: Number,
default: -1,
},
iclass: {
type: [String, Array],
default: ''
},
maxlength: {
type: Number,
default: null,
},
validator: {
type: Object,
default: null,
},
},
computed: {
realid() {
return this.id + this._uid;
},
inputClass() {
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);
},
},
};
</script>

View File

@@ -0,0 +1,137 @@
<template>
<div :class="dclass">
<div>
<p-input
ref="input"
v-model.lazy="prop"
:name="name"
:type="_type"
:class="inputClass"
:color="inputColor"
:value="value"
:disabled="disabled"
:required="required"
@change="$emit('change', $event)"
>
<slot></slot>
<slot slot="extra" name="inputextra"></slot>
</p-input>
</div>
<div class="pointer" @click="select()">
<label v-if="hasSlot('label')" class="pointer">
<slot name="label"></slot>
</label>
<slot name="extra"></slot>
</div>
</div>
</template>
<script>
import PInput from 'pretty-checkbox-vue/input';
export default {
components: {
PInput
},
model: {
prop: 'modelValue',
event: 'change'
},
props: {
name: {
type: String,
default: '',
},
value: {
type: [String, Boolean],
default: '',
},
modelValue: {
type: [String, Boolean],
default: '',
},
iclass: {
type: [String, Array],
default: ''
},
fullClass: {
type: [String, Array],
default: ''
},
dclass: {
type: [String, Array],
default: ''
},
color: {
type: [String, Array],
default: ''
},
disabled: {
type: Boolean,
default: false
},
required: {
type: Boolean,
default: false
}
},
data() {
return {
prop: null
};
},
computed: {
_type() {
if (this.$options.input_type) {
return this.$options.input_type;
}
return 'input';
},
inputClass() {
return this.fullClass !== '' ? this.fullClass : [this.iclass, 'p-default', this.$options.input_iclass];
},
inputColor() {
return this.color !== '' ? this.color : 'primary-o';
},
},
watch: {
modelValue(val) {
this.prop = val;
},
},
mounted() {
this.prop = this.modelValue;
},
methods: {
select() {
if (this.disabled) {
return;
}
switch (this._type)
{
case 'checkbox':
this.$refs.input.$refs.input.checked = ! this.$refs.input.$refs.input.checked;
this.$emit('change', this.$refs.input.$refs.input.checked);
break;
case 'radio':
this.$refs.input.$refs.input.checked = true;
this.$emit('change', this.value);
break;
case 'input':
//this.$refs.input.$refs.input.focus();
}
},
hasSlot (name = 'default') {
return !!this.$slots[ name ] || !!this.$scopedSlots[ name ];
}
}
};
</script>

View File

@@ -0,0 +1,23 @@
<script>
const input = require('./PInput').default;
const radio = {
name: 'radio',
input_type: 'radio',
input_iclass: 'p-round p-thick',
components: input.components,
model: input.model,
props: input.props,
data: input.data,
computed: input.computed,
watch: input.watch,
mounted: input.mounted,
methods: input.methods,
render: input.render,
};
export default radio;
</script>

View File

@@ -0,0 +1,164 @@
<style scoped>
.select {
height: 34px;
transition: all;
transition-duration: 0.2s;
border: 1px solid #c4cdd5;
}
.select:focus {
border: 1px solid #5c6ac4;
}
</style>
<template>
<div :class="{ 'form-group-error': validator && validator.$error }">
<label
v-if="title"
:for="realid"
class="mb2"
:class="{ b: required, error: validator && validator.$error }"
>
{{ title }}
</label>
<select
:id="realid"
ref="select"
:value="selectedOption"
:name="id"
:required="required"
:class="selectClass"
@input="onInput"
>
<template v-if="Array.isArray(options)">
<option
v-for="option in filterExclude(options)"
:key="option.id"
:value="option.id"
>
{{ option.name }}
</option>
</template>
<template v-else>
<optgroup
v-for="(optgroup,index) in options"
:key="index"
:label="optgroup.name"
>
<option
v-for="option in filterExclude(optgroup.options)"
:key="option.id"
:value="option.id"
>
{{ option.name }}
</option>
</optgroup>
</template>
</select>
<small v-if="validator && (validator.$error && validator.required !== undefined && !validator.required)" class="error">
{{ requiredMessage }}
</small>
</div>
</template>
<script>
export default {
props: {
value: {
type: [String, Number],
default: '',
},
options: {
type: [Array, Object],
default: () => [],
},
title: {
type: String,
default: '',
},
label: {
type: String,
default: null,
},
id: {
type: String,
default: '',
},
excludedId: {
type: Number,
default: -1,
},
required: {
type: Boolean,
default: true,
},
iclass: {
type: String,
default: '',
},
validator: {
type: Object,
default: null,
}
},
data() {
return {
selectedOption: null,
};
},
computed: {
realid() {
return this.id + this._uid;
},
selectClass() {
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: {
value: function (newValue) {
this.selectedOption = newValue;
}
},
mounted() {
this.selectedOption = this.value;
},
methods: {
/**
* Filter options
*/
filterExclude: function (options) {
var me = this;
return options.filter(function (option) {
return option.id !== me.excludedId;
});
},
focus() {
this.$refs.select.focus();
},
onInput(event) {
if (this.validator) {
this.validator.$touch();
}
this.$emit('input', event.target.value);
},
},
};
</script>

View File

@@ -0,0 +1,96 @@
<style scoped>
textarea {
transition: all;
transition-duration: 0.2s;
border: 1px solid #c4cdd5;
}
textarea:focus {
border: 1px solid #5c6ac4;
}
</style>
<template>
<div>
<label
v-if="label"
:for="realid"
class="mb2"
:class="{ b: required }"
>
{{ label }}
</label>
<textarea
:id="realid"
v-model="buffer"
autofocus
:required="required"
:name="id"
:placeholder="placeholder"
:rows="rows"
class="br2 f5 w-100 ba b--black-40 pa2 outline-0"
:style="textareaStyle"
@input="$emit('input', buffer)"
></textarea>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: '',
},
label: {
type: String,
default: '',
},
id: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
},
required: {
type: Boolean,
default: true,
},
width: {
type: Number,
default: -1,
},
rows: {
type: Number,
default: 0,
}
},
data() {
return {
buffer: this.value
};
},
computed: {
realid() {
return this.id + this._uid;
},
textareaStyle() {
return this.width >= 0 ? 'width:' + this.width + 'px' : '';
}
},
watch: {
value: function (newValue) {
this.buffer = newValue;
}
},
mounted() {
this.buffer = this.value;
},
};
</script>

View File

@@ -0,0 +1,91 @@
<template>
<div>
<label
v-if="title"
:for="realId"
class="mb2"
:class="{ b: required }"
>
{{ title }}
</label>
<toggle-button
:id="realId"
:name="id"
:class="inputClass"
:sync="true"
:labels="labels"
:disabled="disabled"
:value="selectedOption"
@input="$emit('input', $event)"
@change="$emit('change', $event)"
/>
</div>
</template>
<script>
import { ToggleButton } from 'vue-js-toggle-button';
export default {
components: {
ToggleButton
},
props: {
value: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '',
},
labels: {
type: [Boolean, Object],
default: false,
},
id: {
type: String,
default: '',
},
required: {
type: Boolean,
default: true,
},
disabled: {
type: Boolean,
default: false,
},
iclass: {
type: String,
default: ''
},
},
data() {
return {
selectedOption: null,
};
},
computed: {
realId() {
return this.id + this._uid;
},
inputClass() {
return this.iclass !== '' ? this.iclass : '';
},
},
watch: {
value: function (newValue) {
this.selectedOption = newValue;
}
},
mounted() {
this.selectedOption = this.value;
},
};
</script>