Files
portal/app/resources/js/composables/mockDealEvents.ts
T
Дмитрий 45239f6602 phase2(deal-drawer): DealDetailDrawer - правая панель с деталями сделки
- DealDetailDrawer (v-navigation-drawer right temporary 480px):
  - hero (#id eyebrow + name h5 + close + tel:link + clock + status-chip)
  - section Параметры (2-col grid: Проект/Стоимость/Менеджер/Источник)
  - section Активность (timeline 6 events с iconified vertical-line)
- mockDealEvents.ts: 6 mock-events (created/balance_charged/assigned/viewed/
  status_changed/commented) - соответствуют ActivityLog event-константам v8.7.
- Интеграция в DealsView (@click:row) и KanbanView (через @open-deal от карточки).
- cspell-words.txt: iconified, мапы, резолвятся, резолвером, stub'ить, инлайнен.

Vue3 quirk: v-navigation-drawer требует layout-injection от v-app/v-layout,
но в Vitest vite-plugin-vuetify auto-import не работает. Решение:
- DealsView/KanbanView тесты: stubs:{DealDetailDrawer:true}
- DealDetailDrawer тесты: stubs:{VNavigationDrawer:passthrough-div}

Vitest +8 (всего 79/79 за 7.57s):
- DealDetailDrawer 8 (open=false скрытие, deal=null no-content, hero+id,
  tel:link, status-chip, params, timeline 6 items, emit update:open(false)).

Регресс: lint+type+format OK; vitest 79/79; vite build (drawer инлайнен в
DealsView+KanbanView lazy-chunks); story:build 15/22 за 31.55s; Pest 48/48.

CLAUDE.md v1.25->v1.26, реестр Открытых_вопросов v1.34->v1.35.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:29:11 +03:00

93 lines
3.0 KiB
TypeScript

/**
* Mock activity-events для DealDetailDrawer timeline.
*
* На API будет `GET /api/deals/{id}/events` — выборка из `activity_log` по
* tenant_id (RLS) + deal_id. По схеме v8.7 §10.2 (activity_log table).
*
* Типы событий — соответствуют ActivityLog event-константам:
* `deal.created`, `deal.status_changed`, `deal.viewed`, `deal.commented`,
* `deal.assigned`, `deal.balance_charged` и т.п.
*/
export interface DealEvent {
id: number;
type:
| 'deal.created'
| 'deal.status_changed'
| 'deal.viewed'
| 'deal.commented'
| 'deal.assigned'
| 'deal.balance_charged';
actor: { initials: string; name: string } | null; // null = system
minutesAgo: number;
detail: string;
}
export const MOCK_EVENTS: DealEvent[] = [
{
id: 1,
type: 'deal.created',
actor: null,
minutesAgo: 28,
detail: 'Лид принят с источника Я.Директ → landing-1',
},
{
id: 2,
type: 'deal.balance_charged',
actor: null,
minutesAgo: 28,
detail: 'Списано 1 лид с баланса (стоимость 1 850 ₽)',
},
{
id: 3,
type: 'deal.assigned',
actor: { initials: 'СА', name: 'Система автораспределения' },
minutesAgo: 27,
detail: 'Назначен менеджер: Иван П.',
},
{
id: 4,
type: 'deal.viewed',
actor: { initials: 'ИП', name: 'Иван П.' },
minutesAgo: 18,
detail: 'Менеджер открыл карточку',
},
{
id: 5,
type: 'deal.status_changed',
actor: { initials: 'ИП', name: 'Иван П.' },
minutesAgo: 12,
detail: 'Новые → Просмотрено',
},
{
id: 6,
type: 'deal.commented',
actor: { initials: 'ИП', name: 'Иван П.' },
minutesAgo: 8,
detail: 'Дозвонился, заинтересована — перезвоню после 14:00',
},
];
export function eventTypeLabel(type: DealEvent['type']): string {
const map: Record<DealEvent['type'], string> = {
'deal.created': 'Создана',
'deal.status_changed': 'Смена статуса',
'deal.viewed': 'Просмотр',
'deal.commented': 'Комментарий',
'deal.assigned': 'Назначение',
'deal.balance_charged': 'Списание',
};
return map[type];
}
export function eventTypeIcon(type: DealEvent['type']): string {
const map: Record<DealEvent['type'], string> = {
'deal.created': 'mdi-plus-circle-outline',
'deal.status_changed': 'mdi-swap-horizontal',
'deal.viewed': 'mdi-eye-outline',
'deal.commented': 'mdi-comment-outline',
'deal.assigned': 'mdi-account-arrow-right-outline',
'deal.balance_charged': 'mdi-currency-rub',
};
return map[type];
}