Add Ask Mento, customer mood, and Pipecat speech-to-text.

Ship assistant answers, contact moods, voice dictation, and the related CRM UI so these can be deployed to production.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-03 14:20:55 +03:30
co-authored by Cursor
parent 0dd2966f3c
commit 7757c0837d
43 changed files with 5156 additions and 1741 deletions
+56 -2
View File
@@ -8,8 +8,10 @@ import axios from 'axios';
import { api } from '@/lib/api';
import type { CompanyDto, CreatePersonRequest, PersonDto } from '@mento/shared';
import type { CompanyDto, CreatePersonRequest, PersonDto, PersonMood } from '@mento/shared';
import { PERSON_MOOD_LABELS, PERSON_MOODS } from '@/lib/person-mood';
import CustomFieldsSection from '@/components/CustomFieldsSection.vue';
import CustomerMoodFace from '@/components/CustomerMoodFace.vue';
import EntityTimeline from '@/components/EntityTimeline.vue';
import MentoMemoryPanel from '@/components/MentoMemoryPanel.vue';
@@ -50,7 +52,7 @@ const position = ref('');
const notes = ref('');
const companyId = ref('');
const mood = ref<PersonMood>('newcomer');
const customFields = ref<Record<string, unknown>>({});
const loyaltyPoints = ref(0);
const loyaltyTierName = ref('');
@@ -74,6 +76,7 @@ function fill(p: PersonDto) {
notes.value = p.notes ?? '';
companyId.value = p.company?.id ?? '';
mood.value = p.mood || 'newcomer';
customFields.value = { ...(p.customFields ?? {}) };
loyaltyPoints.value = p.loyaltyPoints ?? 0;
loyaltyTierName.value = p.loyaltyTier?.name ?? '';
@@ -139,6 +142,7 @@ function payload(): CreatePersonRequest {
notes: notes.value || undefined,
companyId: companyId.value || null,
mood: mood.value,
customFields: customFields.value,
};
@@ -304,6 +308,26 @@ onMounted(load);
</div>
<div class="field wide">
<label>وضعیت مشتری</label>
<div class="moods">
<button
v-for="item in PERSON_MOODS"
:key="item"
class="mood"
type="button"
:class="{ on: mood === item }"
@click="mood = item"
>
<CustomerMoodFace :mood="item" :size="44" />
<span>{{ PERSON_MOOD_LABELS[item] }}</span>
</button>
</div>
</div>
<div class="field wide">
<label for="companyId">شرکت</label>
@@ -517,7 +541,37 @@ textarea {
}
.moods {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.55rem;
}
.mood {
display: flex;
align-items: center;
gap: 0.55rem;
min-height: 56px;
padding: 0.4rem 0.65rem;
border: 1px solid #e2e8f0;
border-radius: 14px;
background: #fff;
font: inherit;
text-align: start;
cursor: pointer;
}
.mood.on {
border-color: #14b8a6;
background: #ecfdf5;
box-shadow: 0 0 0 3px #ccfbf1;
}
@media (min-width: 700px) {
.moods {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
@media (max-width: 700px) {