|
1 | 1 | <script lang="ts"> |
2 | 2 | import Spinner from './Icons/Spinner.svelte'; |
3 | 3 | import ExclamationMark from './Icons/ExclamationMark.svelte'; |
4 | | - import { type ProgressData } from '$lib/types'; |
| 4 | + import { type ProgressData, type Column, type RawData } from '$lib/types'; |
5 | 5 | import Table from '$lib/Table.svelte'; |
6 | 6 | import { t, config } from './config'; |
7 | 7 | import ErrorBanner from '$lib/ErrorBanner.svelte'; |
8 | 8 | import Pagination from './Pagination.svelte'; |
| 9 | + import FilterByFailed from './FilterByFailed.svelte'; |
9 | 10 |
|
10 | 11 | const { page_size, data_url } = config; |
11 | 12 |
|
12 | 13 | let error: boolean = $state(false); |
| 14 | + let filters: Column[] | null = $state(null); |
| 15 | + let selectedFilter: number | null = $state(getCurrentFilter()); |
13 | 16 | let data: ProgressData | null = $state(null); |
14 | 17 | let total: number | null = $state(null); |
15 | 18 | let fetching: boolean = $state(true); |
16 | 19 | let errorMessage: string = $state(''); |
17 | 20 | let size: number = $state(parseInt(page_size)); |
18 | 21 | let page: number = $state(getCurrentPage()); |
19 | 22 |
|
| 23 | + function parseInteger(int: string | null): number | null { |
| 24 | + if (int) { |
| 25 | + return Number(int); |
| 26 | + } |
| 27 | + return null; |
| 28 | + } |
| 29 | +
|
20 | 30 | function getCurrentPage(): number { |
21 | 31 | const url = new URL(document.location.href); |
22 | | - const page = Number(url.searchParams.get('page')) || null; |
| 32 | + const page = parseInteger(url.searchParams.get('page')); |
23 | 33 | return page ?? 1; |
24 | 34 | } |
| 35 | + function getCurrentFilter(): number | null { |
| 36 | + const url = new URL(document.location.href); |
| 37 | + const failedAt = parseInteger(url.searchParams.get('failed_at')); |
| 38 | + return failedAt; |
| 39 | + } |
25 | 40 |
|
26 | 41 | function updateUrl(): void { |
27 | 42 | const pageUrl = new URL(document.location.href); |
28 | 43 | pageUrl.searchParams.set('page', String(page)); |
| 44 | + pageUrl.searchParams.set('failed_at', String(selectedFilter)); |
29 | 45 | history.replaceState({}, '', pageUrl); |
30 | 46 | } |
31 | 47 |
|
|
38 | 54 | fetching = true; |
39 | 55 | errorMessage = ''; |
40 | 56 | error = false; |
| 57 | +
|
41 | 58 | const url = new URL(data_url, document.location.href); |
42 | 59 | url.searchParams.set('page', String(page)); |
| 60 | +
|
| 61 | + if (selectedFilter) { |
| 62 | + url.searchParams.set('failed_at', String(selectedFilter)); |
| 63 | + } |
43 | 64 | url.searchParams.set('size', String(size)); |
44 | 65 | fetch(url.toString()) |
45 | 66 | .then((response) => response.json()) |
46 | | - .then(({ data: recievedData, meta }) => { |
| 67 | + .then(({ data: recievedData, meta }: { data: ProgressData | null; meta: RawData }) => { |
47 | 68 | if (recievedData) { |
48 | 69 | total = meta?.total ?? null; |
49 | 70 | data = recievedData; |
| 71 | + filters = recievedData.columns.filter(({ type }) => 'step' === type); |
50 | 72 | } |
51 | 73 | fetching = false; |
52 | 74 | }) |
|
57 | 79 | errorMessage = t('An error occurred while fetching the data'); |
58 | 80 | }); |
59 | 81 | }); |
| 82 | +
|
| 83 | + function selectFilter(event: Event): void { |
| 84 | + // Page is reset on filters, so when filtered we start from scratch, and not accidentally end up on a page that has no content |
| 85 | + page = 1; |
| 86 | + selectedFilter = parseInteger((event.target as HTMLInputElement).value); |
| 87 | + } |
60 | 88 | </script> |
61 | 89 |
|
62 | 90 | {#if error} |
|
74 | 102 | class="flex flex-col border bg-gray-100 dark:bg-gray-900 border-neutral-300 dark:border-neutral-800 rounded-md mb-5" |
75 | 103 | > |
76 | 104 | <div |
77 | | - class="p-3 dark:text-white font-medium bg-gray-200 dark:bg-gray-800 flex items-center border-b border-neutral-300 dark:border-neutral-800" |
| 105 | + class="p-4 dark:text-white justify-between font-medium bg-gray-200 dark:bg-gray-800 flex items-center border-b border-neutral-300 dark:border-neutral-800" |
78 | 106 | > |
79 | | - <ExclamationMark /> |
80 | | - <h2>{t('Failed processes')}</h2> |
81 | | - <span class="ml-2 px-2 py-0.5 text-xs font-semibold rounded-full bg-rose-700 text-white" |
82 | | - >{total ?? '?'}</span |
83 | | - > |
| 107 | + <div class="flex"> |
| 108 | + <ExclamationMark /> |
| 109 | + <h2>{t('Failed processes')}</h2> |
| 110 | + <span |
| 111 | + class="ml-2 px-2 py-0.5 text-xs font-semibold rounded-full self-center bg-rose-700 text-white" |
| 112 | + >{total ?? '?'}</span |
| 113 | + > |
| 114 | + </div> |
| 115 | + <FilterByFailed {selectedFilter} {selectFilter} {filters} /> |
84 | 116 | </div> |
85 | 117 | <div class="p-4 min-h-[450px] flex flex-col justify-between"> |
86 | 118 | <Table columns={data.columns} rows={data.rows}></Table> |
|
0 commit comments