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,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>