diff --git a/web/build/index.html b/web/build/index.html index e0a6034..271c044 100644 --- a/web/build/index.html +++ b/web/build/index.html @@ -4,32 +4,35 @@ - - - - + + + + - + + + + - + - +
+ +
+

{title}

+ {#if description} +

{description}

+ {/if} + {#if children} +
{@render children()}
+ {/if} +
diff --git a/web/src/lib/components/Pagination.svelte b/web/src/lib/components/Pagination.svelte new file mode 100644 index 0000000..7f60b10 --- /dev/null +++ b/web/src/lib/components/Pagination.svelte @@ -0,0 +1,47 @@ + + +{#if totalPages > 1} +
+

+ Showing {(page - 1) * perPage + 1}–{Math.min(page * perPage, total)} of {total} +

+
+ + {#each Array(Math.min(totalPages, 5)) as _, i} + {@const pageNum = page <= 3 ? i + 1 : page - 2 + i} + {#if pageNum <= totalPages} + + {/if} + {/each} + +
+
+{/if} diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte new file mode 100644 index 0000000..7e63004 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte @@ -0,0 +1,27 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte new file mode 100644 index 0000000..a4ce03c --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte @@ -0,0 +1,27 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte new file mode 100644 index 0000000..17b4c6b --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte @@ -0,0 +1,32 @@ + + + + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte new file mode 100644 index 0000000..1af63c0 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte new file mode 100644 index 0000000..4d133b7 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte new file mode 100644 index 0000000..fecf4d2 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte new file mode 100644 index 0000000..a5f8bc8 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte new file mode 100644 index 0000000..3c1d3a2 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte new file mode 100644 index 0000000..f0a19a8 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte new file mode 100644 index 0000000..a801f8d --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte new file mode 100644 index 0000000..b22d1d5 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/alert-dialog.svelte b/web/src/lib/components/ui/alert-dialog/alert-dialog.svelte new file mode 100644 index 0000000..7ea78bb --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/alert-dialog.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/alert-dialog/index.ts b/web/src/lib/components/ui/alert-dialog/index.ts new file mode 100644 index 0000000..f009c26 --- /dev/null +++ b/web/src/lib/components/ui/alert-dialog/index.ts @@ -0,0 +1,40 @@ +import Action from "./alert-dialog-action.svelte"; +import Cancel from "./alert-dialog-cancel.svelte"; +import Content from "./alert-dialog-content.svelte"; +import Description from "./alert-dialog-description.svelte"; +import Footer from "./alert-dialog-footer.svelte"; +import Header from "./alert-dialog-header.svelte"; +import Media from "./alert-dialog-media.svelte"; +import Overlay from "./alert-dialog-overlay.svelte"; +import Portal from "./alert-dialog-portal.svelte"; +import Title from "./alert-dialog-title.svelte"; +import Trigger from "./alert-dialog-trigger.svelte"; +import Root from "./alert-dialog.svelte"; + +export { + Root, + Title, + Action, + Cancel, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Media, + // + Root as AlertDialog, + Title as AlertDialogTitle, + Action as AlertDialogAction, + Cancel as AlertDialogCancel, + Portal as AlertDialogPortal, + Footer as AlertDialogFooter, + Header as AlertDialogHeader, + Trigger as AlertDialogTrigger, + Overlay as AlertDialogOverlay, + Content as AlertDialogContent, + Description as AlertDialogDescription, + Media as AlertDialogMedia, +}; diff --git a/web/src/lib/components/ui/button/button.svelte b/web/src/lib/components/ui/button/button.svelte index c38862e..4066c00 100644 --- a/web/src/lib/components/ui/button/button.svelte +++ b/web/src/lib/components/ui/button/button.svelte @@ -1,17 +1,17 @@ + + diff --git a/web/src/lib/components/ui/sheet/sheet-content.svelte b/web/src/lib/components/ui/sheet/sheet-content.svelte new file mode 100644 index 0000000..1ab6772 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-content.svelte @@ -0,0 +1,55 @@ + + + + + + + + {@render children?.()} + {#if showCloseButton} + + {#snippet child({ props })} + + {/snippet} + + {/if} + + diff --git a/web/src/lib/components/ui/sheet/sheet-description.svelte b/web/src/lib/components/ui/sheet/sheet-description.svelte new file mode 100644 index 0000000..2f663a8 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/sheet/sheet-footer.svelte b/web/src/lib/components/ui/sheet/sheet-footer.svelte new file mode 100644 index 0000000..ad9e07b --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/web/src/lib/components/ui/sheet/sheet-header.svelte b/web/src/lib/components/ui/sheet/sheet-header.svelte new file mode 100644 index 0000000..ebeb288 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/web/src/lib/components/ui/sheet/sheet-overlay.svelte b/web/src/lib/components/ui/sheet/sheet-overlay.svelte new file mode 100644 index 0000000..8d33847 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-overlay.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/sheet/sheet-portal.svelte b/web/src/lib/components/ui/sheet/sheet-portal.svelte new file mode 100644 index 0000000..f3085a3 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/sheet/sheet-title.svelte b/web/src/lib/components/ui/sheet/sheet-title.svelte new file mode 100644 index 0000000..60c8d5b --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web/src/lib/components/ui/sheet/sheet-trigger.svelte b/web/src/lib/components/ui/sheet/sheet-trigger.svelte new file mode 100644 index 0000000..e266975 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/sheet/sheet.svelte b/web/src/lib/components/ui/sheet/sheet.svelte new file mode 100644 index 0000000..5bf9783 --- /dev/null +++ b/web/src/lib/components/ui/sheet/sheet.svelte @@ -0,0 +1,7 @@ + + + diff --git a/web/src/lib/components/ui/sonner/index.ts b/web/src/lib/components/ui/sonner/index.ts new file mode 100644 index 0000000..1ad9f4a --- /dev/null +++ b/web/src/lib/components/ui/sonner/index.ts @@ -0,0 +1 @@ +export { default as Toaster } from "./sonner.svelte"; diff --git a/web/src/lib/components/ui/sonner/sonner.svelte b/web/src/lib/components/ui/sonner/sonner.svelte new file mode 100644 index 0000000..33161a6 --- /dev/null +++ b/web/src/lib/components/ui/sonner/sonner.svelte @@ -0,0 +1,34 @@ + + + + {#snippet loadingIcon()} + + {/snippet} + {#snippet successIcon()} + + {/snippet} + {#snippet errorIcon()} + + {/snippet} + {#snippet infoIcon()} + + {/snippet} + {#snippet warningIcon()} + + {/snippet} + diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index e7f844f..6a4d274 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -10,6 +10,8 @@ export function cn(...inputs: ClassValue[]) { } export type WithoutChildren = T extends { children?: Snippet } ? Omit : T; +export type WithoutChild = T extends { child?: Snippet } ? Omit : T; +export type WithoutChildrenOrChild = WithoutChildren>; export type WithElementRef = WithoutChildren & { ref?: U | null; children?: Snippet; diff --git a/web/src/routes/(dashboard)/+layout.svelte b/web/src/routes/(dashboard)/+layout.svelte index 19051e5..b499c8b 100644 --- a/web/src/routes/(dashboard)/+layout.svelte +++ b/web/src/routes/(dashboard)/+layout.svelte @@ -4,6 +4,7 @@ import { getAuthStore } from '$lib/stores/auth.svelte'; import { destroyWsClient } from '$lib/ws'; import type { UserInfo } from '$lib/api'; + import * as Sheet from '$lib/components/ui/sheet/index.js'; interface Props { title?: string; @@ -44,7 +45,33 @@
- (mobileMenuOpen = false)}> + + + + + + + TrapFall + + + + + TrapFall
- {#if mobileMenuOpen} - - {/if} diff --git a/web/src/routes/(dashboard)/issues/+page.svelte b/web/src/routes/(dashboard)/issues/+page.svelte index 74e33d9..0d14bdb 100644 --- a/web/src/routes/(dashboard)/issues/+page.svelte +++ b/web/src/routes/(dashboard)/issues/+page.svelte @@ -7,6 +7,8 @@ import { Badge } from '$lib/components/ui/badge/index.js'; import { Button } from '$lib/components/ui/button/index.js'; import { Skeleton } from '$lib/components/ui/skeleton/index.js'; + import EmptyState from '$lib/components/EmptyState.svelte'; + import Pagination from '$lib/components/Pagination.svelte'; import { Table, TableBody, @@ -271,18 +273,14 @@

{error}

{:else if issues.length === 0} -
-

No issues found

-

- {#if searchQuery} - No results for "{searchQuery}" in {projects.find(p => p.slug === selectedProject)?.name || selectedProject}. Try switching projects. - {:else if filterStatus || filterLevel} - Try adjusting your filters. - {:else} - Send errors to your DSN and they'll appear here. - {/if} -

-
+ p.slug === selectedProject)?.name || selectedProject}. Try switching projects.` + : filterStatus || filterLevel + ? 'Try adjusting your filters.' + : "Send errors to your DSN and they'll appear here."} + /> {:else}
@@ -329,43 +327,6 @@ - {#if totalPages > 1} -
-

- Showing {(currentPage - 1) * perPage + 1}–{Math.min(currentPage * perPage, totalIssues)} of {totalIssues} -

-
- - {#each Array(Math.min(totalPages, 5)) as _, i} - {@const pageNum = currentPage <= 3 ? i + 1 : currentPage - 2 + i} - {#if pageNum <= totalPages} - - {/if} - {/each} - -
-
- {/if} + {/if} diff --git a/web/src/routes/(dashboard)/issues/[issueId]/+page.svelte b/web/src/routes/(dashboard)/issues/[issueId]/+page.svelte index 10fe67a..ee58b7a 100644 --- a/web/src/routes/(dashboard)/issues/[issueId]/+page.svelte +++ b/web/src/routes/(dashboard)/issues/[issueId]/+page.svelte @@ -7,6 +7,8 @@ import { Button } from '$lib/components/ui/button/index.js'; import { Card, CardContent } from '$lib/components/ui/card/index.js'; import { Skeleton } from '$lib/components/ui/skeleton/index.js'; + import EmptyState from '$lib/components/EmptyState.svelte'; + import Pagination from '$lib/components/Pagination.svelte'; import { Table, TableBody, @@ -233,9 +235,10 @@ {#if events.length === 0} -
-

No events recorded for this issue yet.

-
+ {:else}
@@ -268,31 +271,13 @@ - {#if totalEventPages > 1} -
-

- Page {eventsPage} of {totalEventPages} -

-
- - -
-
- {/if} + {/if} {/if} diff --git a/web/src/routes/(dashboard)/projects/+page.svelte b/web/src/routes/(dashboard)/projects/+page.svelte index f78d292..dcda57c 100644 --- a/web/src/routes/(dashboard)/projects/+page.svelte +++ b/web/src/routes/(dashboard)/projects/+page.svelte @@ -7,6 +7,9 @@ import { Input } from '$lib/components/ui/input/index.js'; import { Label } from '$lib/components/ui/label/index.js'; import { Skeleton } from '$lib/components/ui/skeleton/index.js'; + import EmptyState from '$lib/components/EmptyState.svelte'; + import * as AlertDialog from '$lib/components/ui/alert-dialog/index.js'; + import { toast } from 'svelte-sonner'; let projects: Project[] = $state([]); let loading = $state(true); @@ -27,6 +30,9 @@ let editName = $state(''); + /** Pending destructive action awaiting confirmation in the AlertDialog. */ + let pending: { action: 'delete' | 'rotate'; slug: string; name: string } | null = $state(null); + async function toggleDsn(project: Project) { const id = project.id; if (showDsn[id]) { @@ -81,6 +87,7 @@ try { const slug = newProjectSlug || undefined; await api.createProject(newProjectName, slug); + toast.success('Project created'); newProjectName = ''; newProjectSlug = ''; showAddForm = false; @@ -96,6 +103,7 @@ if (!editName.trim()) return; try { await api.updateProject(slug, editName.trim()); + toast.success('Project renamed'); showEditForm = null; editName = ''; openMenu = null; @@ -109,9 +117,10 @@ openMenu = null; try { await api.archiveProject(slug); + toast.success('Project archived'); await loadProjects(); } catch (e: any) { - error = e?.message || 'Failed to archive project'; + toast.error(e?.message || 'Failed to archive project'); } } @@ -119,24 +128,27 @@ openMenu = null; try { await api.unarchiveProject(slug); + toast.success('Project unarchived'); await loadProjects(); } catch (e: any) { - error = e?.message || 'Failed to unarchive project'; + toast.error(e?.message || 'Failed to unarchive project'); } } async function handleDelete(slug: string) { - if (!confirm('Permanently delete this project and all its data? This cannot be undone.')) return; + pending = null; openMenu = null; try { await api.deleteProject(slug); + toast.success('Project deleted'); await loadProjects(); } catch (e: any) { - error = e?.message || 'Failed to delete project (must be archived first)'; + toast.error(e?.message || 'Failed to delete project (must be archived first)'); } } async function handleRotateDsn(slug: string) { + pending = null; openMenu = null; try { const updated = await api.rotateDsn(slug); @@ -145,8 +157,11 @@ // 'Show' button displays the real value without an extra fetch. revealedDsn[updated.id] = updated.dsn; showDsn[updated.id] = true; + toast.success('DSN rotated', { + description: 'Update your SDK with the new DSN — the old one no longer works.' + }); } catch (e: any) { - error = e?.message || 'Failed to rotate DSN'; + toast.error(e?.message || 'Failed to rotate DSN'); } } @@ -225,14 +240,12 @@ {/each} {:else if displayedProjects.length === 0} -
-

- {tab === 'active' ? 'No active projects' : 'No archived projects'} -

-

- {tab === 'active' ? 'Create a project to start capturing errors.' : 'Archived projects will appear here.'} -

-
+ {:else}
{#each displayedProjects as project (project.id)} @@ -284,7 +297,7 @@ @@ -303,7 +316,7 @@ @@ -352,7 +365,36 @@

- {/each} -
- {/if} - + {/each} + + {/if} + + + + (pending = o ? pending : null)}> + + + + {pending?.action === 'delete' ? `Delete ${pending?.name}?` : `Rotate DSN for ${pending?.name}?`} + + + {#if pending?.action === 'delete'} + This permanently removes the project and all its captured events. This cannot be undone. + {:else} + The current DSN stops working immediately. Any SDK still using it will fail to send events until updated. + {/if} + + + + Cancel + (pending?.action === 'delete' ? handleDelete(pending.slug) : handleRotateDsn(pending!.slug))} + > + {pending?.action === 'delete' ? 'Delete project' : 'Rotate DSN'} + + + + diff --git a/web/src/routes/(dashboard)/release-health/+page.svelte b/web/src/routes/(dashboard)/release-health/+page.svelte index 8934b97..1091453 100644 --- a/web/src/routes/(dashboard)/release-health/+page.svelte +++ b/web/src/routes/(dashboard)/release-health/+page.svelte @@ -7,6 +7,8 @@ import { Button } from '$lib/components/ui/button/index.js'; import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card/index.js'; import { Skeleton } from '$lib/components/ui/skeleton/index.js'; + import EmptyState from '$lib/components/EmptyState.svelte'; + import Pagination from '$lib/components/Pagination.svelte'; import { Table, TableBody, @@ -342,12 +344,10 @@

{error}

{:else if sessions.length === 0} -
-

No session data yet

-

- Ensure your Sentry SDK has release configured. -

-
+ {:else}
@@ -401,43 +401,6 @@ - {#if totalPages > 1} -
-

- Showing {(currentPage - 1) * perPage + 1}–{Math.min(currentPage * perPage, totalSessions)} of {totalSessions} -

-
- - {#each Array(Math.min(totalPages, 5)) as _, i} - {@const pageNum = currentPage <= 3 ? i + 1 : currentPage - 2 + i} - {#if pageNum <= totalPages} - - {/if} - {/each} - -
-
- {/if} + {/if} diff --git a/web/src/routes/(dashboard)/rules/+page.svelte b/web/src/routes/(dashboard)/rules/+page.svelte index 9d144aa..fe1b687 100644 --- a/web/src/routes/(dashboard)/rules/+page.svelte +++ b/web/src/routes/(dashboard)/rules/+page.svelte @@ -13,6 +13,8 @@ toggleAlertRule } from '$lib/api'; import { Badge } from '$lib/components/ui/badge/index.js'; + import * as AlertDialog from '$lib/components/ui/alert-dialog/index.js'; + import { toast } from 'svelte-sonner'; import { Button } from '$lib/components/ui/button/index.js'; import { Card } from '$lib/components/ui/card/index.js'; import { Input } from '$lib/components/ui/input/index.js'; @@ -24,6 +26,8 @@ let selectedProject: string = $state(''); let loading = $state(true); let error = $state(''); + /** Rule id awaiting delete confirmation in the AlertDialog. */ + let pendingDelete: AlertRule | null = $state(null); let showForm = $state(false); let formName = $state(''); @@ -96,27 +100,33 @@ showForm = false; formName = ''; formWebhookUrl = ''; + toast.success('Rule created'); await loadRules(); } catch (e: any) { - error = e?.message || 'Failed to create rule'; + toast.error(e?.message || 'Failed to create rule'); } } async function handleToggle(rule: AlertRule) { try { await toggleAlertRule(rule.id, !rule.enabled); + toast.success(rule.enabled ? 'Rule disabled' : 'Rule enabled'); await loadRules(); } catch (e: any) { - error = e?.message || 'Failed to toggle'; + toast.error(e?.message || 'Failed to toggle rule'); } } - async function handleDelete(ruleId: string) { + async function handleDelete() { + if (!pendingDelete) return; + const id = pendingDelete.id; + pendingDelete = null; try { - await deleteAlertRule(ruleId); + await deleteAlertRule(id); + toast.success('Rule deleted'); await loadRules(); } catch (e: any) { - error = e?.message || 'Failed to delete'; + toast.error(e?.message || 'Failed to delete rule'); } } @@ -232,7 +242,7 @@ - @@ -241,4 +251,25 @@ {/each} {/if} + + + (pendingDelete = o ? pendingDelete : null)}> + + + Delete rule "{pendingDelete?.name}"? + + Alerts matching this rule will no longer be sent. This cannot be undone. + + + + Cancel + + Delete rule + + + + diff --git a/web/src/routes/(dashboard)/settings/+page.svelte b/web/src/routes/(dashboard)/settings/+page.svelte index 832c1f2..064a929 100644 --- a/web/src/routes/(dashboard)/settings/+page.svelte +++ b/web/src/routes/(dashboard)/settings/+page.svelte @@ -7,6 +7,7 @@ import { Input } from '$lib/components/ui/input/index.js'; import { Label } from '$lib/components/ui/label/index.js'; import { Separator } from '$lib/components/ui/separator/index.js'; + import { toast } from 'svelte-sonner'; const auth = getAuthStore(); let user = $derived(auth.user); @@ -34,7 +35,7 @@ current_password: currentPassword, new_password: newPassword }); - passwordSuccess = 'Password updated successfully.'; + toast.success('Password updated'); currentPassword = ''; newPassword = ''; confirmPassword = ''; @@ -92,9 +93,6 @@ {#if passwordError}

{passwordError}

{/if} - {#if passwordSuccess} -

{passwordSuccess}

- {/if} diff --git a/web/src/routes/(dashboard)/transactions/+page.svelte b/web/src/routes/(dashboard)/transactions/+page.svelte index 570f250..4f320bc 100644 --- a/web/src/routes/(dashboard)/transactions/+page.svelte +++ b/web/src/routes/(dashboard)/transactions/+page.svelte @@ -7,6 +7,8 @@ import { Button } from '$lib/components/ui/button/index.js'; import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card/index.js'; import { Skeleton } from '$lib/components/ui/skeleton/index.js'; + import EmptyState from '$lib/components/EmptyState.svelte'; + import Pagination from '$lib/components/Pagination.svelte'; import { Table, TableBody, @@ -166,12 +168,10 @@

{error}

{:else if transactions.length === 0} -
-

No transactions found

-

- Performance data will appear here once transactions are received. -

-
+ {:else}
@@ -212,43 +212,6 @@ - {#if totalPages > 1} -
-

- Showing {(currentPage - 1) * perPage + 1}–{Math.min(currentPage * perPage, totalTransactions)} of {totalTransactions} -

-
- - {#each Array(Math.min(totalPages, 5)) as _, i} - {@const pageNum = currentPage <= 3 ? i + 1 : currentPage - 2 + i} - {#if pageNum <= totalPages} - - {/if} - {/each} - -
-
- {/if} + {/if} diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index ab0dcc9..90f5d06 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -1,6 +1,7 @@ + {@render children()}