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,47 @@
<template>
<span v-cy-name="'last-talked-to'">{{ lastCalledMessage }}</span>
</template>
<script>
export default {
props: {
hash: {
type: String,
default: '',
},
initialValue: {
type: String,
default: '',
}
},
data() {
return {
lastCalled: '',
};
},
computed: {
lastCalledMessage() {
if (!this.initialValue && !this.lastCalled) {
return this.$t('people.last_called_empty');
}
if (!this.lastCalled) {
return this.$t('people.last_talked_to', {date: this.initialValue});
}
return this.$t('people.last_talked_to', {date: this.lastCalled});
}
},
methods: {
getLastCalled() {
axios.get('people/' + this.hash + '/calls/last')
.then(response => {
this.lastCalled = response.data.last_talked_to;
});
}
}
};
</script>

View File

@@ -0,0 +1,417 @@
<template>
<div>
<div class="">
<h3 class="mb2">
&#8199;{{ $t('people.call_title') }}
<span class="fr relative" style="top: -7px;">
<a v-if="displayLogCall === false" v-cy-name="'add-call-button'" class="btn edit-information" href="" @click.prevent="displayLogCall = true">
{{ $t('people.call_button') }}
</a>
<a v-if="displayLogCall" class="btn edit-information" href="" @click.prevent="displayLogCall = false">
{{ $t('app.cancel') }}
</a>
</span>
</h3>
</div>
<!-- BLANK STATE -->
<div v-if="!displayLogCall && calls.length === 0" v-cy-name="'calls-blank-state'" class="w-100">
<div class="bg-near-white tc pa3 br2 ba b--light-gray">
<p>{{ $t('people.call_blank_title', { name: name }) }}</p>
<a class="pointer" href="" @click.prevent="displayLogCall = true">
{{ $t('people.call_button') }}
</a>
</div>
</div>
<!-- LOG A CALL -->
<transition name="fade">
<div v-if="displayLogCall" v-cy-name="'log-call-form'" class="ba br3 mb3 pa3 b--black-40">
<div class="dt dt--fixed pb3 mb3 mb0-ns">
<!-- WHEN -->
<div class="dtc pr2">
<p class="mb2 b">
{{ $t('people.modal_call_exact_date') }}
</p>
<div class="di mr3">
<div class="dib">
<form-date
v-model="newCall.called_at"
:default-date="todayDate"
:locale="locale"
/>
</div>
</div>
</div>
<!-- WHO CALLED -->
<div class="dtc">
<p class="mb2 b">
{{ $t('people.modal_call_who_called') }}
</p>
<div class="dt">
<div class="dt-row">
<form-radio
v-model="newCall.contact_called"
:name="'contact_called'"
:value="false"
:iclass="'mr1'"
:dclass="'dtc mr3'"
>
{{ $t('people.call_you_called') }}
</form-radio>
<form-radio
v-model="newCall.contact_called"
:name="'contact_called'"
:value="true"
:iclass="'mr1'"
:dclass="'dtc mr3'"
>
{{ $t('people.call_he_called', { name : name }) }}
</form-radio>
</div>
</div>
</div>
</div>
<!-- CONTENT -->
<div>
<form-textarea
v-model="newCall.content"
:required="true"
:label="$t('people.modal_call_comment')"
:rows="4"
:placeholder="$t('people.conversation_add_content')"
/>
<p class="f6">
{{ $t('app.markdown_description') }} <a href="https://guides.github.com/features/mastering-markdown/" target="_blank" rel="noopener noreferrer">
{{ $t('app.markdown_link') }}
</a>
</p>
</div>
<!-- EMOTIONS -->
<div class="bb b--gray-monica pb3">
<label class="b">
{{ $t('people.modal_call_emotion') }}
</label>
<emotion class="pv2" @update="updateEmotionsList" />
</div>
<!-- ACTIONS -->
<div class="pt3">
<div class="flex-ns justify-between">
<div class="">
<a class="btn btn-secondary tc w-auto-ns w-100 mb2 pb0-ns" href="" @click.prevent="displayLogCall = false; resetFields()">
{{ $t('app.cancel') }}
</a>
</div>
<div class="">
<button v-cy-name="'save-call-button'" class="btn btn-primary w-auto-ns w-100 mb2 pb0-ns" @click.prevent="store()">
{{ $t('app.add') }}
</button>
</div>
</div>
</div>
</div>
</transition>
<!-- LIST OF CALLS -->
<div v-cy-name="'calls-body'" v-cy-items="calls.map(c => c.id)">
<div v-for="call in calls" :key="call.id" v-cy-name="'call-body-'+call.id" class="ba br2 b--black-10 br--top w-100 mb2">
<div v-show="editCallId !== call.id" class="pa2">
<span v-if="!call.content">
{{ $t('people.call_blank_desc', { name: call.contact.first_name }) }}
</span>
<span v-if="call.content" dir="auto" class="markdown" v-html="compiledMarkdown(call.content)"></span>
</div>
<!-- INLINE UPDATE DIV -->
<div v-show="editCallId === call.id" class="pa2">
<div>
<form-textarea
v-model="editCall.content"
:label="$t('people.modal_call_comment')"
:rows="4"
iclass="br2 f5 w-100 ba b--black-40 pa2 outline-0"
@contentChange="updateEditCallContent($event)"
/>
<p class="f6">
{{ $t('app.markdown_description') }}
</p>
</div>
<!-- WHO CALLED -->
<div class="pb3 mb3 mb0-ns">
<p class="mb2">
{{ $t('people.modal_call_who_called') }}
</p>
<div class="di mr3">
<input :id="'you' + call.id" v-model="editCall.contact_called" type="radio" class="mr1" :name="'contact_called' + call.id"
:value="false"
/>
<p class="f6">
{{ $t('app.markdown_description') }}
</p>
</div>
<!-- WHO CALLED -->
<div class="pb3 mb3 mb0-ns">
<p class="mb2">
{{ $t('people.modal_call_who_called') }}
</p>
<div class="di mr3">
<input :id="'you' + call.id" v-model="editCall.contact_called" type="radio" class="mr1" :name="'contact_called' + call.id"
:value="false"
/>
<label :for="'you' + call.id" class="pointer">
{{ $t('people.call_you_called') }}
</label>
</div>
<div class="di mr3">
<input :id="'contact' + call.id" v-model="editCall.contact_called" type="radio" class="mr1" :name="'contact_called' + call.id"
:value="true"
/>
<label :for="'contact' + call.id" class="pointer">
{{ $t('people.call_he_called', { name : name }) }}
</label>
</div>
</div>
<!-- EMOTIONS -->
<div class="bb b--gray-monica pb3 mb3">
<label class="b">
{{ $t('people.modal_call_emotion') }}
</label>
<emotion class="pv2" :initial-emotions="call.emotions" @update="updateEmotionsList" />
</div>
<!-- ACTIONS -->
<div class="">
<div class="flex-ns justify-between">
<div class="">
<a class="btn btn-secondary tc w-auto-ns w-100 mb2 pb0-ns" href="" @click.prevent="editCallId = 0">
{{ $t('app.cancel') }}
</a>
</div>
<div class="">
<button class="btn btn-primary w-auto-ns w-100 mb2 pb0-ns" @click.prevent="update()">
{{ $t('app.update') }}
</button>
</div>
</div>
</div>
</div>
</div>
<!-- ADDITIONAL INFORMATION -->
<div class="pa2 cf bt b--black-10 br--bottom f7 lh-copy">
<div class="w-70" :class="[ dirltr ? 'fl' : 'fr' ]">
<span :class="[ dirltr ? 'mr3' : 'ml3' ]">
{{ call.called_at | moment }}
</span>
<span :class="[ dirltr ? 'mr3' : 'ml3' ]">
{{ call.contact_called ? $t('people.call_he_called', { name : name }) : $t('people.call_you_called') }}
</span>
<!-- EMOTION LIST -->
<span v-if="call.emotions.length !== 0">
<span :class="[ dirltr ? 'mr2' : 'ml2' ]">
{{ $t('people.call_emotions') }}
</span>
<ul class="di">
<li v-for="emotion in call.emotions" :key="emotion.id" class="di">
{{ $t('app.emotion_' + emotion.name) }}
</li>
</ul>
</span>
</div>
<div :class="[ dirltr ? 'fl tr' : 'fr tl' ]" class="w-30">
<a :class="[ dirltr ? 'mr2' : 'ml2' ]" class="pointer " href="" @click.prevent="showEditBox(call)">
{{ $t('app.update') }}
</a>
<a v-show="destroyCallId !== call.id" v-cy-name="'delete-call-button-'+call.id" class="pointer" href="" @click.prevent="showDestroyCall(call)">
{{ $t('app.delete') }}
</a>
<ul v-show="destroyCallId === call.id" class="di">
<li class="di">
<a class="pointer mr1" href="" @click.prevent="destroyCallId = 0">
{{ $t('app.cancel') }}
</a>
</li>
<li class="di">
<a v-cy-name="'delete-call-confirm-button-'+call.id" class="pointer red" href="" @click.prevent="destroyCall(call)">
{{ $t('app.delete_confirm') }}
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import moment from 'moment';
import Emotion from '../Emotion.vue';
export default {
components: {
Emotion,
},
filters: {
moment: function (date) {
return moment.utc(date).format('LL');
}
},
props: {
hash: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
},
data() {
return {
calls: [],
displayLogCall: false,
todayDate: '',
editCallId: 0,
destroyCallId: 0,
chosenEmotions: [],
newCall: {
content: '',
called_at: '',
contact_called: false,
emotions: [],
},
editCall: {
content: '',
contact_called: false,
emotions: [],
}
};
},
computed: {
dirltr() {
return this.$root.htmldir === 'ltr';
},
locale() {
return this.$root.locale;
}
},
mounted() {
this.prepareComponent(this.hash);
},
methods: {
prepareComponent(hash) {
this.getCalls();
this.todayDate = moment().format('YYYY-MM-DD');
this.newCall.called_at = this.todayDate;
},
compiledMarkdown (text) {
return text !== undefined && text !== null ? marked(text, { sanitize: true }) : '';
},
resetFields() {
this.newCall.content = '';
this.newCall.called_at = this.todayDate;
},
getCalls() {
axios.get('people/' + this.hash + '/calls')
.then(response => {
this.calls = response.data.data;
});
},
store() {
axios.post('people/' + this.hash + '/calls', this.newCall)
.then(response => {
this.getCalls();
this.resetFields();
this.displayLogCall = false;
this.chosenEmotions = [];
this.updateLastCalled();
this.$notify({
group: 'main',
title: this.$t('people.calls_add_success'),
text: '',
type: 'success'
});
});
},
update() {
axios.put('people/' + this.hash + '/calls/' + this.editCallId, this.editCall)
.then(response => {
this.getCalls();
this.editCallId = 0;
this.chosenEmotions = [];
this.updateLastCalled();
this.$notify({
group: 'main',
title: this.$t('app.default_save_success'),
text: '',
type: 'success'
});
});
},
updateLastCalled() {
this.$parent.$refs.lastCalledAttribute.getLastCalled();
},
showEditBox(call) {
this.editCallId = call.id;
this.editCall.content = call.content;
this.editCall.contact_called = call.contact_called;
this.editCall.called_at = moment.utc(call.called_at).format('YYYY-MM-DD');
},
updateDate(updatedContent) {
this.newCall.called_at = updatedContent;
},
showDestroyCall(call) {
this.destroyCallId = call.id;
},
destroyCall(call) {
axios.delete('people/' + this.hash + '/calls/' + this.destroyCallId)
.then(response => {
this.calls.splice(this.calls.indexOf(call), 1);
this.updateLastCalled();
});
},
updateEmotionsList: function(emotions) {
this.chosenEmotions = emotions;
this.newCall.emotions = [];
this.editCall.emotions = [];
// filter the list of emotions to populate a new array
// containing only the emotion ids and not the entire objetcs
for (let i = 0; i < this.chosenEmotions.length; i++) {
this.newCall.emotions.push(this.chosenEmotions[i].id);
this.editCall.emotions.push(this.chosenEmotions[i].id);
}
}
}
};
</script>