From bb2fe2f34d054fffa411fb29da858ed52d9943b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Wed, 15 Jul 2026 16:40:46 +0300 Subject: [PATCH] =?UTF-8?q?feat(sales):=20=D0=B4=D0=B8=D0=B0=D0=BB=D0=BE?= =?UTF-8?q?=D0=B3=20=D0=BA=D0=B0=D1=80=D1=82=D0=BE=D1=87=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B3=D0=BD=D0=BE=D0=B7=D0=B0=20=E2=80=94=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=D1=83=D0=BB=D1=8C=D1=82=D0=B0=D1=82=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=B3=D0=BE=D0=B2=D0=BE=D1=80=D0=B0=20+=20=D0=B2?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Этап 1 Task 10. Селектор результата, поля по стадии, отказ скрыт при user, обязательные причина/время. Тесты через vm (v-dialog телепортит контент). Co-Authored-By: Claude Opus 4.8 --- .../components/sales/SalesProspectDialog.vue | 135 ++++++++++++++++++ .../Frontend/SalesProspectDialog.spec.ts | 77 ++++++++++ docs/observer/STATUS.md | 8 +- 3 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 app/resources/js/components/sales/SalesProspectDialog.vue create mode 100644 app/tests/Frontend/SalesProspectDialog.spec.ts diff --git a/app/resources/js/components/sales/SalesProspectDialog.vue b/app/resources/js/components/sales/SalesProspectDialog.vue new file mode 100644 index 00000000..49dc9609 --- /dev/null +++ b/app/resources/js/components/sales/SalesProspectDialog.vue @@ -0,0 +1,135 @@ + + + diff --git a/app/tests/Frontend/SalesProspectDialog.spec.ts b/app/tests/Frontend/SalesProspectDialog.spec.ts new file mode 100644 index 00000000..9b544c56 --- /dev/null +++ b/app/tests/Frontend/SalesProspectDialog.spec.ts @@ -0,0 +1,77 @@ +import { mount } from '@vue/test-utils'; +import { createVuetify } from 'vuetify'; +import { describe, expect, it } from 'vitest'; +import SalesProspectDialog from '../../resources/js/components/sales/SalesProspectDialog.vue'; +import type { SalesProspect } from '../../resources/js/api/sales'; + +const vuetify = createVuetify(); + +function prospect(overrides: Partial = {}): SalesProspect { + return { + id: 1, + sales_user_id: 1, + stage: 'new', + firm_name: 'ООО Тест', + city: 'Ростов', + phone: '+7800', + site: 'test.ru', + inn: '6161', + rating_label: 'горячая', + payload: {}, + next_call_at: null, + reason: null, + registered_email: null, + notes: null, + ...overrides, + }; +} + +interface DialogVm { + action: string; + reason: string; + nextCallAt: string; + siteHref: string | null; + availableActions: { value: string; title: string }[]; + submit: () => void; +} + +function factory(p: SalesProspect) { + return mount(SalesProspectDialog, { + props: { modelValue: true, prospect: p }, + global: { plugins: [vuetify] }, + }); +} + +describe('SalesProspectDialog', () => { + it('siteHref добавляет https к домену', () => { + const w = factory(prospect()); + expect((w.vm as unknown as DialogVm).siteHref).toBe('https://test.ru'); + }); + + it('для stage=user действие «Отказ» недоступно', () => { + const w = factory(prospect({ stage: 'user' })); + const values = (w.vm as unknown as DialogVm).availableActions.map((a) => a.value); + expect(values).not.toContain('rejected'); + }); + + it('для обычной стадии «Отказ» доступен', () => { + const w = factory(prospect({ stage: 'negotiation' })); + const values = (w.vm as unknown as DialogVm).availableActions.map((a) => a.value); + expect(values).toContain('rejected'); + }); + + it('«Переговоры» без даты — submit не эмитит save', () => { + const w = factory(prospect()); + (w.vm as unknown as DialogVm).action = 'negotiation'; + (w.vm as unknown as DialogVm).submit(); + expect(w.emitted('save')).toBeFalsy(); + }); + + it('«Отказ» с причиной — эмитит save с action=rejected', () => { + const w = factory(prospect({ stage: 'negotiation' })); + (w.vm as unknown as DialogVm).action = 'rejected'; + (w.vm as unknown as DialogVm).reason = 'дорого'; + (w.vm as unknown as DialogVm).submit(); + expect(w.emitted('save')?.[0]?.[0]).toMatchObject({ action: 'rejected', reason: 'дорого' }); + }); +}); diff --git a/docs/observer/STATUS.md b/docs/observer/STATUS.md index 79b2ff08..63b8b652 100644 --- a/docs/observer/STATUS.md +++ b/docs/observer/STATUS.md @@ -1,6 +1,6 @@ # Brain Status (auto-generated) -Last updated: 2026-07-15T13:36:04.742Z +Last updated: 2026-07-15T13:38:18.903Z | Контролёр | Состояние | Детали | |---|---|---| @@ -112,9 +112,9 @@ Episodes since last run: 542 / threshold: 10 | PID | Имя | CPU-время | Возраст | |---|---|---|---| -| 3488 | MsMpEng | 6.91ч | 0.0ч | -| 9756 | Code | 3.18ч | 0.0ч | -| 1320 | svchost | 1.33ч | 1328341.6ч | +| 3488 | MsMpEng | 6.92ч | NaNч | +| 9756 | Code | 3.19ч | NaNч | +| 1320 | svchost | 1.33ч | NaNч | ⚠️ Проверь, не «осиротевшие» ли это процессы от завершённых Claude-сессий.