6416ec3789
feat(sales): периоды сегодня/вчера/7/30 дней календарём, пополнения за период, скролл доски наверх Замечания владельца по сводке и доске. ПЕРИОДЫ: - Резолвер понимает today/yesterday/d7/d30; «7 дней» = сегодня и 6 предыдущих. Месячные this/prev/prev2 сервер принимает по-прежнему. - По умолчанию 30 дней. - ПОЧИНЕНО: выбор «Произвольный» без дат ронял запрос (500). Теперь понятный 422, а на фронте период применяется только когда отмечены ОБЕ даты. - Произвольный выбирается календарём-диапазоном, не руками; порядок дат неважен. ПОПОЛНЕНИЯ ЗА ПЕРИОД (переиспользован готовый topupsRub): - Сводка: плашка «Пополнили баланс» рядом с «Σ баланс». - Мои клиенты: колонка «Пополнил». Заодно даёт число, которое видимо меняется при смене периода. ДОСКА: горизонтальная полоса прокрутки поднята НАД колонками (колонки высокие, системная полоса уезжала за экран); синхронизация в обе стороны, ResizeObserver на приезжающие карточки. Спека §21 (включая §21.4 — как по коду заполняется «Требуют внимания»). Гейты: Pest 257/257 sales+unit, Vitest 1417, vue-tsc чисто, Larastan 0 в своих. TDD. LEFTHOOK_EXCLUDE: larastan/cspell падают на файлах параллельной сессии. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @
97 lines
3.5 KiB
TypeScript
97 lines
3.5 KiB
TypeScript
import { mount } from '@vue/test-utils';
|
|
import { createVuetify } from 'vuetify';
|
|
import { createPinia, setActivePinia } from 'pinia';
|
|
import { describe, expect, it, beforeEach } from 'vitest';
|
|
import PeriodPicker from '../../resources/js/components/sales/PeriodPicker.vue';
|
|
import { useSalesPeriodStore } from '../../resources/js/stores/salesPeriod';
|
|
|
|
const vuetify = createVuetify();
|
|
|
|
interface PickerVm {
|
|
options: { value: string; label: string }[];
|
|
pick: (v: string) => void;
|
|
customRange: string[];
|
|
applyCustom: () => void;
|
|
menuOpen: boolean;
|
|
}
|
|
|
|
function factory() {
|
|
const w = mount(PeriodPicker, { global: { plugins: [vuetify] } });
|
|
return { w, vm: w.vm as unknown as PickerVm };
|
|
}
|
|
|
|
describe('PeriodPicker', () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createPinia());
|
|
document.body.innerHTML = '';
|
|
});
|
|
|
|
it('предлагает сегодня, вчера, 7 дней, 30 дней и произвольный', () => {
|
|
const { vm } = factory();
|
|
expect(vm.options.map((o) => o.value)).toEqual(['today', 'yesterday', 'd7', 'd30', 'custom']);
|
|
expect(vm.options.map((o) => o.label)).toEqual([
|
|
'Сегодня',
|
|
'Вчера',
|
|
'7 дней',
|
|
'30 дней',
|
|
'Произвольный период',
|
|
]);
|
|
});
|
|
|
|
it('по умолчанию — 30 дней', () => {
|
|
factory();
|
|
expect(useSalesPeriodStore().kind).toBe('d30');
|
|
});
|
|
|
|
it('выбор готового периода сразу уходит в запрос', () => {
|
|
const { vm } = factory();
|
|
vm.pick('yesterday');
|
|
|
|
const store = useSalesPeriodStore();
|
|
expect(store.kind).toBe('yesterday');
|
|
expect(store.queryParams).toEqual({ period: 'yesterday' });
|
|
});
|
|
|
|
it('произвольный период применяется только после выбора обеих дат', () => {
|
|
const { vm } = factory();
|
|
vm.pick('custom');
|
|
const store = useSalesPeriodStore();
|
|
|
|
// Одна дата — период ещё не применяем, иначе сервер ответит ошибкой.
|
|
vm.customRange = ['2026-07-01'];
|
|
vm.applyCustom();
|
|
expect(store.kind).not.toBe('custom');
|
|
|
|
vm.customRange = ['2026-07-01', '2026-07-10'];
|
|
vm.applyCustom();
|
|
expect(store.queryParams).toEqual({ period: 'custom', from: '2026-07-01', to: '2026-07-10' });
|
|
});
|
|
|
|
it('календарь отдаёт даты в любом порядке — раскладываем от меньшей к большей', () => {
|
|
const { vm } = factory();
|
|
vm.pick('custom');
|
|
vm.customRange = ['2026-07-10', '2026-07-01'];
|
|
vm.applyCustom();
|
|
|
|
expect(useSalesPeriodStore().queryParams).toEqual({
|
|
period: 'custom',
|
|
from: '2026-07-01',
|
|
to: '2026-07-10',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('salesPeriod store', () => {
|
|
beforeEach(() => setActivePinia(createPinia()));
|
|
|
|
it('подписи периодов человеческие', () => {
|
|
const store = useSalesPeriodStore();
|
|
store.setPeriod({ kind: 'today' });
|
|
expect(store.label).toBe('Сегодня');
|
|
store.setPeriod({ kind: 'd7' });
|
|
expect(store.label).toBe('7 дней');
|
|
store.setPeriod({ kind: 'custom', from: '2026-07-01', to: '2026-07-10' });
|
|
expect(store.label).toBe('01.07.2026 — 10.07.2026');
|
|
});
|
|
});
|