feat: add refresh functionality for bug reports and improve pagination logic

This commit is contained in:
Flavio Fois
2026-02-14 22:58:23 +01:00
parent c6c27f2f30
commit c2052595cb
3 changed files with 57 additions and 36 deletions

View File

@@ -1,7 +1,7 @@
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { bugReports, bugReportFiles } from '$lib/schema'; import { bugReports, bugReportFiles } from '$lib/schema';
import { eq, like, or, count, sql, desc } from 'drizzle-orm'; import { eq, like, or, count, sql, desc, and } from 'drizzle-orm';
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const page = Math.max(1, Number(url.searchParams.get('page')) || 1); const page = Math.max(1, Number(url.searchParams.get('page')) || 1);
@@ -10,44 +10,31 @@ export const load: PageServerLoad = async ({ url }) => {
const search = url.searchParams.get('search') || ''; const search = url.searchParams.get('search') || '';
const conditions = []; const conditions = [];
if (status && ['new', 'in_review', 'resolved', 'closed'].includes(status)) { if (status && ['new', 'in_review', 'resolved', 'closed'].includes(status)) {
conditions.push(eq(bugReports.status, status as 'new' | 'in_review' | 'resolved' | 'closed')); conditions.push(eq(bugReports.status, status as 'new' | 'in_review' | 'resolved' | 'closed'));
} }
if (search) { if (search) {
const pattern = `%${search}%`;
conditions.push( conditions.push(
or( or(
like(bugReports.hostname, pattern), like(bugReports.hostname, `%${search}%`),
like(bugReports.os_user, pattern), like(bugReports.os_user, `%${search}%`),
like(bugReports.name, pattern), like(bugReports.name, `%${search}%`),
like(bugReports.email, pattern) like(bugReports.email, `%${search}%`)
)! )
); );
} }
const where = conditions.length > 0 const where = conditions.length > 0 ? and(...conditions) : undefined;
? conditions.length === 1
? conditions[0]
: sql`${conditions[0]} AND ${conditions[1]}`
: undefined;
const [totalResult] = await db // Get total count
.select({ count: count() }) const [{ total }] = await db
.select({ total: count() })
.from(bugReports) .from(bugReports)
.where(where); .where(where);
const total = totalResult.count; // Get paginated reports with file count
const totalPages = Math.max(1, Math.ceil(total / pageSize));
const fileCountSubquery = db
.select({
report_id: bugReportFiles.report_id,
file_count: count().as('file_count')
})
.from(bugReportFiles)
.groupBy(bugReportFiles.report_id)
.as('fc');
const reports = await db const reports = await db
.select({ .select({
id: bugReports.id, id: bugReports.id,
@@ -57,21 +44,27 @@ export const load: PageServerLoad = async ({ url }) => {
os_user: bugReports.os_user, os_user: bugReports.os_user,
status: bugReports.status, status: bugReports.status,
created_at: bugReports.created_at, created_at: bugReports.created_at,
file_count: sql<number>`COALESCE(${fileCountSubquery.file_count}, 0)`.as('file_count') file_count: count(bugReportFiles.id)
}) })
.from(bugReports) .from(bugReports)
.leftJoin(fileCountSubquery, eq(bugReports.id, fileCountSubquery.report_id)) .leftJoin(bugReportFiles, eq(bugReports.id, bugReportFiles.report_id))
.where(where) .where(where)
.groupBy(bugReports.id)
.orderBy(desc(bugReports.created_at)) .orderBy(desc(bugReports.created_at))
.limit(pageSize) .limit(pageSize)
.offset((page - 1) * pageSize); .offset((page - 1) * pageSize);
return { return {
reports: reports.map((r) => ({ reports,
...r, pagination: {
created_at: r.created_at.toISOString() page,
})), pageSize,
pagination: { page, pageSize, total, totalPages }, total,
filters: { status, search } totalPages: Math.ceil(total / pageSize)
},
filters: {
status,
search
}
}; };
}; };

View File

@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto, invalidateAll } from '$app/navigation';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { statusColors, statusLabels, formatDate } from '$lib/utils'; import { statusColors, statusLabels, formatDate } from '$lib/utils';
import { Search, ChevronLeft, ChevronRight, Filter, Paperclip } from 'lucide-svelte'; import { Search, ChevronLeft, ChevronRight, Filter, Paperclip, RefreshCcw } from 'lucide-svelte';
let { data } = $props(); let { data } = $props();
@@ -33,6 +33,14 @@
statusFilter = ''; statusFilter = '';
goto('/'); goto('/');
} }
async function refreshReports() {
try {
await invalidateAll();
} catch (err) {
console.error('Failed to refresh reports:', err);
}
}
</script> </script>
<div class="space-y-4"> <div class="space-y-4">
@@ -66,6 +74,13 @@
<Filter class="h-4 w-4" /> <Filter class="h-4 w-4" />
Filter Filter
</button> </button>
<button
onclick={refreshReports}
class="inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90"
>
<RefreshCcw class="h-4 w-4" />
Filter
</button>
{#if data.filters.search || data.filters.status} {#if data.filters.search || data.filters.status}
<button <button
onclick={clearFilters} onclick={clearFilters}

View File

@@ -0,0 +1,13 @@
import type { RequestHandler } from './$types';
import { json } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { bugReports } from '$lib/schema';
import { count } from 'drizzle-orm';
export const GET: RequestHandler = async () => {
const [{ total }] = await db
.select({ total: count() })
.from(bugReports);
return json({ total });
};