Edit: Add from phone contacts
This commit is contained in:
@@ -5,6 +5,12 @@ import axios from 'axios';
|
||||
import { api } from '@/lib/api';
|
||||
import type { CompanyDto, CreatePersonRequest, PersonDto, PersonMood } from '@mento/shared';
|
||||
import { PERSON_MOOD_LABELS, PERSON_MOODS } from '@/lib/person-mood';
|
||||
import {
|
||||
isContactPickerSupported,
|
||||
parseVCardFile,
|
||||
pickOnePhoneContact,
|
||||
type PhoneContactDraft,
|
||||
} from '@/lib/phone-contacts';
|
||||
import CustomFieldsSection from '@/components/CustomFieldsSection.vue';
|
||||
import CustomerMoodFace from '@/components/CustomerMoodFace.vue';
|
||||
import EntityTimeline from '@/components/EntityTimeline.vue';
|
||||
@@ -56,10 +62,83 @@ const customFields = ref<Record<string, unknown>>({});
|
||||
const loyaltyPoints = ref(0);
|
||||
const loyaltyTierName = ref('');
|
||||
|
||||
const contactPickerSupported = isContactPickerSupported();
|
||||
const pickingContact = ref(false);
|
||||
const contactFileInput = ref<HTMLInputElement | null>(null);
|
||||
|
||||
const backTo = computed(() =>
|
||||
isNew.value ? { name: 'contacts' as const } : { name: 'person-detail' as const, params: { id: id.value } },
|
||||
);
|
||||
|
||||
/** فقط موبایل را اجباری پر میکند؛ نام و بقیه فقط اگر خالی باشند. */
|
||||
function applyContactDraft(draft: PhoneContactDraft) {
|
||||
if (draft.mobile) {
|
||||
mobile.value = draft.mobile;
|
||||
} else if (draft.phone && !mobile.value.trim()) {
|
||||
mobile.value = draft.phone;
|
||||
}
|
||||
if (!firstName.value.trim() && draft.firstName && draft.firstName !== 'بدوننام') {
|
||||
firstName.value = draft.firstName;
|
||||
}
|
||||
if (!lastName.value.trim() && draft.lastName) {
|
||||
lastName.value = draft.lastName;
|
||||
}
|
||||
if (!phone.value.trim() && draft.phone && draft.phone !== draft.mobile) {
|
||||
phone.value = draft.phone;
|
||||
}
|
||||
if (!email.value.trim() && draft.email) {
|
||||
email.value = draft.email;
|
||||
}
|
||||
}
|
||||
|
||||
async function fillFromPhoneContact() {
|
||||
if (pickingContact.value || saving.value) return;
|
||||
pickingContact.value = true;
|
||||
error.value = '';
|
||||
try {
|
||||
if (!contactPickerSupported) {
|
||||
contactFileInput.value?.click();
|
||||
return;
|
||||
}
|
||||
const draft = await pickOnePhoneContact();
|
||||
if (!draft) return;
|
||||
applyContactDraft(draft);
|
||||
} catch (e) {
|
||||
const name = e instanceof DOMException ? e.name : '';
|
||||
const message = e instanceof Error ? e.message : '';
|
||||
if (name === 'AbortError') return;
|
||||
if (message === 'CONTACT_PICKER_UNSUPPORTED' || name === 'NotSupportedError') {
|
||||
contactFileInput.value?.click();
|
||||
return;
|
||||
}
|
||||
error.value = 'انتخاب مخاطب از گوشی ناموفق بود.';
|
||||
} finally {
|
||||
pickingContact.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function onContactVcfFile(ev: Event) {
|
||||
const el = ev.target as HTMLInputElement;
|
||||
const file = el.files?.[0];
|
||||
el.value = '';
|
||||
if (!file) return;
|
||||
pickingContact.value = true;
|
||||
error.value = '';
|
||||
try {
|
||||
const rows = await parseVCardFile(file);
|
||||
const draft = rows[0];
|
||||
if (!draft) {
|
||||
error.value = 'مخاطبی در فایل پیدا نشد.';
|
||||
return;
|
||||
}
|
||||
applyContactDraft(draft);
|
||||
} catch {
|
||||
error.value = 'خواندن فایل مخاطب (.vcf) ناموفق بود.';
|
||||
} finally {
|
||||
pickingContact.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function fill(p: PersonDto) {
|
||||
firstName.value = p.firstName;
|
||||
lastName.value = p.lastName;
|
||||
@@ -205,9 +284,28 @@ onMounted(load);
|
||||
</UiField>
|
||||
|
||||
<UiField label="موبایل" for-id="mobile">
|
||||
<template #label-extra>
|
||||
<button
|
||||
type="button"
|
||||
class="from-contact"
|
||||
:disabled="pickingContact || saving"
|
||||
@click="fillFromPhoneContact"
|
||||
>
|
||||
<UiIcon name="phone" :size="14" />
|
||||
{{ pickingContact ? '…' : 'از مخاطبین' }}
|
||||
</button>
|
||||
</template>
|
||||
<UiInput id="mobile" v-model="mobile" type="tel" />
|
||||
</UiField>
|
||||
|
||||
<input
|
||||
ref="contactFileInput"
|
||||
class="hidden-file"
|
||||
type="file"
|
||||
accept=".vcf,text/vcard,text/x-vcard"
|
||||
@change="onContactVcfFile"
|
||||
/>
|
||||
|
||||
<UiField label="تلفن" for-id="phone">
|
||||
<UiInput id="phone" v-model="phone" type="tel" />
|
||||
</UiField>
|
||||
@@ -367,6 +465,30 @@ onMounted(load);
|
||||
margin-top: var(--sp-4);
|
||||
}
|
||||
|
||||
.from-contact {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--accent-deep);
|
||||
font: inherit;
|
||||
font-size: var(--fs-xs);
|
||||
font-weight: var(--fw-bold);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.from-contact:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.hidden-file {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 901px) {
|
||||
.mob-only {
|
||||
display: none !important;
|
||||
|
||||
Reference in New Issue
Block a user