refactor: replace custom CRM with Monica fork
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
This commit is contained in:
58
resources/js/components/partials/Avatar.img.vue
Normal file
58
resources/js/components/partials/Avatar.img.vue
Normal 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>
|
||||
38
resources/js/components/partials/Avatar.vue
Normal file
38
resources/js/components/partials/Avatar.vue
Normal 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>
|
||||
74
resources/js/components/partials/Confirm.vue
Normal file
74
resources/js/components/partials/Confirm.vue
Normal 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>
|
||||
51
resources/js/components/partials/Error.vue
Normal file
51
resources/js/components/partials/Error.vue
Normal 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>
|
||||
255
resources/js/components/partials/SpecialDate.vue
Normal file
255
resources/js/components/partials/SpecialDate.vue
Normal 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>
|
||||
133
resources/js/components/partials/SpecialDeceased.vue
Normal file
133
resources/js/components/partials/SpecialDeceased.vue
Normal 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>
|
||||
23
resources/js/components/partials/form/Checkbox.vue
Normal file
23
resources/js/components/partials/form/Checkbox.vue
Normal 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>
|
||||
206
resources/js/components/partials/form/Date.vue
Normal file
206
resources/js/components/partials/form/Date.vue
Normal 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>
|
||||
181
resources/js/components/partials/form/Input.vue
Normal file
181
resources/js/components/partials/form/Input.vue
Normal 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>
|
||||
137
resources/js/components/partials/form/PInput.vue
Normal file
137
resources/js/components/partials/form/PInput.vue
Normal 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>
|
||||
23
resources/js/components/partials/form/Radio.vue
Normal file
23
resources/js/components/partials/form/Radio.vue
Normal 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>
|
||||
164
resources/js/components/partials/form/Select.vue
Normal file
164
resources/js/components/partials/form/Select.vue
Normal 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>
|
||||
96
resources/js/components/partials/form/Textarea.vue
Normal file
96
resources/js/components/partials/form/Textarea.vue
Normal 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>
|
||||
91
resources/js/components/partials/form/Toggle.vue
Normal file
91
resources/js/components/partials/form/Toggle.vue
Normal 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>
|
||||
Reference in New Issue
Block a user