mirror of
https://github.com/Xevion/banner.git
synced 2026-01-31 06:23:37 -06:00
feat: show search duration and result count feedback
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export interface SearchMeta {
|
||||||
|
totalCount: number;
|
||||||
|
durationMs: number;
|
||||||
|
timestamp: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { meta }: { meta: SearchMeta | null } = $props();
|
||||||
|
|
||||||
|
let formattedTime = $derived(
|
||||||
|
meta
|
||||||
|
? meta.timestamp.toLocaleTimeString(undefined, {
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
})
|
||||||
|
: ""
|
||||||
|
);
|
||||||
|
|
||||||
|
let countLabel = $derived(meta ? meta.totalCount.toLocaleString() : "");
|
||||||
|
let resultNoun = $derived(meta ? (meta.totalCount !== 1 ? "results" : "result") : "");
|
||||||
|
let durationLabel = $derived(meta ? `${Math.round(meta.durationMs)}ms` : "");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if meta}
|
||||||
|
<p
|
||||||
|
class="pl-1 text-xs"
|
||||||
|
title="Last searched at {formattedTime}"
|
||||||
|
>
|
||||||
|
<span class="text-muted-foreground/70">{countLabel}</span>
|
||||||
|
<span class="text-muted-foreground/35">{resultNoun} in</span>
|
||||||
|
<span class="text-muted-foreground/70">{durationLabel}</span>
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from "$lib/api";
|
} from "$lib/api";
|
||||||
import type { SortingState } from "@tanstack/table-core";
|
import type { SortingState } from "@tanstack/table-core";
|
||||||
import SearchFilters from "$lib/components/SearchFilters.svelte";
|
import SearchFilters from "$lib/components/SearchFilters.svelte";
|
||||||
|
import SearchStatus, { type SearchMeta } from "$lib/components/SearchStatus.svelte";
|
||||||
import CourseTable from "$lib/components/CourseTable.svelte";
|
import CourseTable from "$lib/components/CourseTable.svelte";
|
||||||
import Pagination from "$lib/components/Pagination.svelte";
|
import Pagination from "$lib/components/Pagination.svelte";
|
||||||
import Footer from "$lib/components/Footer.svelte";
|
import Footer from "$lib/components/Footer.svelte";
|
||||||
@@ -56,6 +57,7 @@ let subjectMap: Record<string, string> = $derived(
|
|||||||
Object.fromEntries(subjects.map((s) => [s.code, s.description]))
|
Object.fromEntries(subjects.map((s) => [s.code, s.description]))
|
||||||
);
|
);
|
||||||
let searchResult: SearchResponse | null = $state(null);
|
let searchResult: SearchResponse | null = $state(null);
|
||||||
|
let searchMeta: SearchMeta | null = $state(null);
|
||||||
let loading = $state(false);
|
let loading = $state(false);
|
||||||
let error = $state<string | null>(null);
|
let error = $state<string | null>(null);
|
||||||
|
|
||||||
@@ -169,6 +171,7 @@ async function performSearch(
|
|||||||
if (sortDir && sortBy) params.set("sort_dir", sortDir);
|
if (sortDir && sortBy) params.set("sort_dir", sortDir);
|
||||||
goto(`?${params.toString()}`, { replaceState: true, noScroll: true, keepFocus: true });
|
goto(`?${params.toString()}`, { replaceState: true, noScroll: true, keepFocus: true });
|
||||||
|
|
||||||
|
const t0 = performance.now();
|
||||||
try {
|
try {
|
||||||
searchResult = await client.searchCourses({
|
searchResult = await client.searchCourses({
|
||||||
term,
|
term,
|
||||||
@@ -180,6 +183,11 @@ async function performSearch(
|
|||||||
sort_by: sortBy,
|
sort_by: sortBy,
|
||||||
sort_dir: sortDir,
|
sort_dir: sortDir,
|
||||||
});
|
});
|
||||||
|
searchMeta = {
|
||||||
|
totalCount: searchResult.totalCount,
|
||||||
|
durationMs: performance.now() - t0,
|
||||||
|
timestamp: new Date(),
|
||||||
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e instanceof Error ? e.message : "Search failed";
|
error = e instanceof Error ? e.message : "Search failed";
|
||||||
} finally {
|
} finally {
|
||||||
@@ -199,6 +207,9 @@ function handlePageChange(newOffset: number) {
|
|||||||
<h1 class="text-2xl font-semibold text-foreground">UTSA Course Search</h1>
|
<h1 class="text-2xl font-semibold text-foreground">UTSA Course Search</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Search status + Filters -->
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<SearchStatus meta={searchMeta} />
|
||||||
<!-- Filters -->
|
<!-- Filters -->
|
||||||
<SearchFilters
|
<SearchFilters
|
||||||
terms={data.terms}
|
terms={data.terms}
|
||||||
@@ -208,6 +219,7 @@ function handlePageChange(newOffset: number) {
|
|||||||
bind:query
|
bind:query
|
||||||
bind:openOnly
|
bind:openOnly
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Results -->
|
<!-- Results -->
|
||||||
{#if error}
|
{#if error}
|
||||||
|
|||||||
Reference in New Issue
Block a user