From 49e4ac0fe25dd5e2622fc08487cc21ccc0405ce2 Mon Sep 17 00:00:00 2001 From: busya Date: Sun, 5 Jul 2026 15:50:51 +0300 Subject: [PATCH] refactor(frontend): integrate compact filters into reports page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename page title "Отчеты задач" → "Центр статусов" - Remove standalone quick-status filter block (moved to FilterBar) - Remove inline filter/sort/time-range HTML (replaced by FilterBar) - Replace statusTotal() with quickStatusCounts - Pass quickStatusCounts and onQuickStatusToggle to FilterBar - Error recovery button text "Повторить" → "Повторить загрузку" --- frontend/src/routes/reports/+page.svelte | 204 +++++------------------ 1 file changed, 46 insertions(+), 158 deletions(-) diff --git a/frontend/src/routes/reports/+page.svelte b/frontend/src/routes/reports/+page.svelte index 435e3950..16e3b9fd 100644 --- a/frontend/src/routes/reports/+page.svelte +++ b/frontend/src/routes/reports/+page.svelte @@ -1,13 +1,13 @@ - + - + - + @@ -19,27 +19,15 @@ import { onMount } from 'svelte'; import { TaskCenterModel } from '$lib/models/TaskCenterModel.svelte.ts'; import SummaryPanel from '$lib/components/reports/SummaryPanel.svelte'; - import ReportsList from '$lib/components/reports/ReportsList.svelte'; - import { PageHeader, Button, EmptyState, Pagination, Input, Select } from '$lib/ui'; + import FilterBar from '$lib/components/reports/FilterBar.svelte'; + import TaskList from '$lib/components/reports/TaskList.svelte'; + import { PageHeader, Button, Pagination } from '$lib/ui'; import { taskDrawerStore } from '$lib/stores/taskDrawer.svelte.js'; import type { PageData } from './+page.ts'; import type { TaskCenterFilter } from '$types/reports'; let { data }: { data: PageData } = $props(); const m = new TaskCenterModel(); - const sortOptions = [ - { value: 'updated_at', label: 'По обновлению' }, - { value: 'created_at', label: 'По созданию' }, - { value: 'status', label: 'По статусу' }, - { value: 'task_type', label: 'По типу' }, - ]; - const timeRangeOptions = [ - { value: 'all', label: 'Всё время' }, - { value: '1h', label: 'Час' }, - { value: '24h', label: '24 часа' }, - { value: '7d', label: '7 дней' }, - { value: '30d', label: '30 дней' }, - ]; const drawerOpen = $derived(Boolean(taskDrawerStore.value?.isOpen)); onMount(() => { @@ -65,34 +53,20 @@ } }); - // ── Search debounce ── - let searchTimeout: ReturnType | undefined; - - function handleSearchInput(e: Event & { currentTarget: HTMLInputElement }): void { - clearTimeout(searchTimeout); - const value = e.currentTarget.value; - searchTimeout = setTimeout(() => { - m.applyFilter({ search: value }); - }, 350); - } - - function handleSortChange(e: Event & { currentTarget: HTMLSelectElement }): void { - m.applyFilter({ sort_by: e.currentTarget.value as TaskCenterFilter['sort_by'] }); - } - - function handleTimeRangeChange(e: Event & { currentTarget: HTMLSelectElement }): void { - m.applyFilter({ time_range: e.currentTarget.value as TaskCenterFilter['time_range'] }); - } - - function statusTotal(status: 'failed' | 'in_progress' | 'success'): number { - const summary = m.visibleSummary; - if (!summary) return 0; - return summary.by_type.reduce((sum, type) => { - if (status === 'failed') return sum + type.counts.failed; - if (status === 'success') return sum + type.counts.success; - return sum + type.counts.pending + type.counts.running + type.counts.awaiting_input; - }, 0); - } + const quickStatusCounts = $derived({ + failed: (() => { + const s = m.visibleSummary; + return s ? s.by_type.reduce((sum, t) => sum + t.counts.failed, 0) : 0; + })(), + in_progress: (() => { + const s = m.visibleSummary; + return s ? s.by_type.reduce((sum, t) => sum + t.counts.pending + t.counts.running + t.counts.awaiting_input, 0) : 0; + })(), + success: (() => { + const s = m.visibleSummary; + return s ? s.by_type.reduce((sum, t) => sum + t.counts.success, 0) : 0; + })(), + }); function toggleStatus(status: TaskCenterFilter['statuses'][number]): void { m.applyFilter({ statuses: m.filters.statuses.includes(status) ? [] : [status] }); @@ -104,7 +78,7 @@ ? 'max-w-7xl md:max-w-[max(24rem,calc(100vw-35rem))] md:ml-4 md:mr-auto' : 'max-w-7xl'}" > - +
{#if m.wsConnected} @@ -124,7 +98,7 @@ {#if m.screenState === 'error'}
Не удалось загрузить данные задач. Сервер временно недоступен. - +
{/if} @@ -143,117 +117,31 @@ onReconnect={() => m.connectWebSocket()} activeFilters={{ task_types: m.filters.task_types, statuses: m.filters.statuses }} /> - {#if m.screenState === 'ready' || m.screenState === 'disconnected'} -
- - - + ) => m.applyFilter(partial)} + onClearFilters={() => m.clearFilters()} + quickStatusCounts={quickStatusCounts} + onQuickStatusToggle={toggleStatus} + /> + + m.selectTask(taskId)} + onClearFilters={() => m.clearFilters()} + /> + {#if m.totalPages > 1 && !m.isEmpty && !m.isFilteredEmpty} +
+
{/if} - - -
-
- -
-
- - -
-
- - Показано {m.filteredTasks.length} из {m.totalItems} задач - - {#if m.hasActiveFilters} - - {/if} -
-
- - -
- {#if m.screenState === 'loading'} -
- {#each [1, 2, 3, 4, 5] as n (n)} -
- {/each} -
- {:else if m.isEmpty} - - {:else if m.isFilteredEmpty} - m.clearFilters()} - /> - {:else} - m.selectTask(e.report.task_id || e.report.report_id)} - /> - {#if m.totalPages > 1} -
- -
- {/if} - {/if} -