68428f16c6
feat(sales): карточка кандидата — девять полей владельца + правка контактных лиц
Владелец задал список полей карточки и попросил дать менеджеру записывать
контактных лиц прямо в карточке: поиск отдаёт 5-7 ПРЕДПОЛАГАЕМЫХ номеров
директора, живой из них выясняется обзвоном; а если директор перенаправил
к маркетологу — менеджер фиксирует и его, людей может быть 2-3.
- Карточка показывает РОВНО девять полей в его порядке: Юрлицо, Сайт, ИНН фирмы,
Адрес, Телефон общий, Каналы, Бюджет, Директор, Тел. директора (предполагаемый).
Убраны Оценка, Достоверность, Реклама, Запросы в Директе, ОГРН, Статус юрлица,
Личный ИНН директора, Почта директора — данные остаются в payload.
- PATCH /api/sales/prospects/{id}/contacts — список заменяется целиком, пустой
стирает всех; права как у update() (менеджер свои, начальник любые); главный
телефон карточки из поиска не трогается.
- ProspectContactsEditor.vue + utils/prospectContacts.ts — один редактор контактов
на диалог создания и карточку; диалог создания переведён на него.
- Закрывает хвост §16: у 24 живых карточек из поиска контактных лиц не было,
теперь дозаполняются руками.
Спека §18. Гейты: Pest 223/223 sales, Vitest 196 файлов / 1397 тестов,
vue-tsc чисто, Larastan 0 в своих файлах. TDD RED→GREEN на каждом шаге.
LEFTHOOK_EXCLUDE: larastan/cspell падают на файлах параллельной сессии
tests/Feature/Admin/*Balances*, свои файлы проверены отдельно и чисты.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
42 lines
1.8 KiB
TypeScript
42 lines
1.8 KiB
TypeScript
import type { ProspectContact } from '../api/sales';
|
|
|
|
/**
|
|
* Контактное лицо в форме: те же поля, но position всегда строка (v-model не любит null),
|
|
* а телефоны — редактируемый список с пустыми заготовками.
|
|
*/
|
|
export interface ContactForm {
|
|
name: string;
|
|
position: string;
|
|
phones: string[];
|
|
}
|
|
|
|
export function emptyContact(): ContactForm {
|
|
return { name: '', position: '', phones: [''] };
|
|
}
|
|
|
|
/** Из сохранённых контактов делаем форму. Пусто → одна пустая строка, чтобы было куда писать. */
|
|
export function toForm(contacts: ProspectContact[] | null | undefined): ContactForm[] {
|
|
if (!contacts || contacts.length === 0) return [emptyContact()];
|
|
return contacts.map((c) => ({
|
|
name: c.name ?? '',
|
|
position: c.position ?? '',
|
|
phones: c.phones && c.phones.length > 0 ? [...c.phones] : [''],
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Форма → то, что уходит на сервер: пустые телефоны выброшены, люди без имени
|
|
* и без телефонов не отправляются. Сервер делает то же самое — здесь это чтобы
|
|
* не гонять по сети заведомый мусор.
|
|
*/
|
|
export function cleanContacts(list: ContactForm[]): ProspectContact[] {
|
|
const out: ProspectContact[] = [];
|
|
for (const c of list) {
|
|
const name = c.name.trim();
|
|
const phones = c.phones.map((p) => p.trim()).filter((p) => p !== '');
|
|
if (name === '' && phones.length === 0) continue;
|
|
out.push({ name, position: c.position.trim() === '' ? null : c.position.trim(), phones });
|
|
}
|
|
return out;
|
|
}
|