Skip to content

Commit 9f68a0e

Browse files
authored
feat: new error page with buttons (#92)
* feat: new error page with buttons * feat: new error handling comp * feat: use svelte base
1 parent ebbe97b commit 9f68a0e

File tree

7 files changed

+185
-10
lines changed

7 files changed

+185
-10
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<script lang="ts">
2+
import { page } from '$app/state';
3+
import Button from '$lib/components/ui/button/button.svelte';
4+
import * as Alert from '$lib/components/ui/alert';
5+
import IconAlertCircle from 'phosphor-icons-svelte/IconWarningCircleRegular.svelte';
6+
import Spinner from '$lib/components/ui/spinner/spinner.svelte';
7+
import { base } from '$app/paths';
8+
9+
let { error: propError, status: propStatus } = $props<{
10+
error?: string;
11+
status?: number;
12+
}>();
13+
14+
let reload = $state(false);
15+
16+
let finalError = $derived(propError ?? page.error ?? 'error fetching error');
17+
18+
let finalStatus = $derived(propStatus ?? page.status ?? 500);
19+
20+
let errorMessage = $derived(() => {
21+
if (typeof finalError === 'string') return finalError;
22+
return finalError?.message ?? 'error fetching error';
23+
});
24+
</script>
25+
26+
<main
27+
class="absolute top-1/2 left-1/2 flex w-1/2 -translate-x-1/2 -translate-y-1/2 flex-col items-center gap-4"
28+
>
29+
<Alert.Root>
30+
<IconAlertCircle />
31+
<Alert.Title>Oops! Something went wrong.</Alert.Title>
32+
<Alert.Description>
33+
<h1>Status: <span class="text-destructive">{finalStatus}</span></h1>
34+
<p>Error: <span class="text-destructive">{errorMessage()}</span></p>
35+
</Alert.Description>
36+
</Alert.Root>
37+
38+
<ol class="mt-4 flex gap-2">
39+
<Button href={base}>Go Home</Button>
40+
<Button onclick={() => window.history.back()}>Go Back</Button>
41+
<Button onclick={() => (window.location.reload(), (reload = true))} class="relative">
42+
<Spinner
43+
class="absolute top-1/2 left-1/2 flex w-1/2 -translate-x-1/2 -translate-y-1/2 {reload
44+
? 'opacity-100'
45+
: 'opacity-0'}"
46+
/>
47+
<span class={reload ? 'opacity-0' : 'opacity-100'}>Retry</span>
48+
</Button>
49+
</ol>
50+
</main>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script lang="ts">
2+
import type { HTMLAttributes } from "svelte/elements";
3+
import { cn, type WithElementRef } from "$lib/utils.js";
4+
5+
let {
6+
ref = $bindable(null),
7+
class: className,
8+
children,
9+
...restProps
10+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11+
</script>
12+
13+
<div
14+
bind:this={ref}
15+
data-slot="alert-description"
16+
class={cn(
17+
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
18+
className
19+
)}
20+
{...restProps}
21+
>
22+
{@render children?.()}
23+
</div>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script lang="ts">
2+
import type { HTMLAttributes } from "svelte/elements";
3+
import { cn, type WithElementRef } from "$lib/utils.js";
4+
5+
let {
6+
ref = $bindable(null),
7+
class: className,
8+
children,
9+
...restProps
10+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
11+
</script>
12+
13+
<div
14+
bind:this={ref}
15+
data-slot="alert-title"
16+
class={cn("col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", className)}
17+
{...restProps}
18+
>
19+
{@render children?.()}
20+
</div>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<script lang="ts" module>
2+
import { type VariantProps, tv } from "tailwind-variants";
3+
4+
export const alertVariants = tv({
5+
base: "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
6+
variants: {
7+
variant: {
8+
default: "bg-card text-card-foreground",
9+
destructive:
10+
"text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current",
11+
},
12+
},
13+
defaultVariants: {
14+
variant: "default",
15+
},
16+
});
17+
18+
export type AlertVariant = VariantProps<typeof alertVariants>["variant"];
19+
</script>
20+
21+
<script lang="ts">
22+
import type { HTMLAttributes } from "svelte/elements";
23+
import { cn, type WithElementRef } from "$lib/utils.js";
24+
25+
let {
26+
ref = $bindable(null),
27+
class: className,
28+
variant = "default",
29+
children,
30+
...restProps
31+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
32+
variant?: AlertVariant;
33+
} = $props();
34+
</script>
35+
36+
<div
37+
bind:this={ref}
38+
data-slot="alert"
39+
class={cn(alertVariants({ variant }), className)}
40+
{...restProps}
41+
role="alert"
42+
>
43+
{@render children?.()}
44+
</div>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Root from "./alert.svelte";
2+
import Description from "./alert-description.svelte";
3+
import Title from "./alert-title.svelte";
4+
export { alertVariants, type AlertVariant } from "./alert.svelte";
5+
6+
export {
7+
Root,
8+
Description,
9+
Title,
10+
//
11+
Root as Alert,
12+
Description as AlertDescription,
13+
Title as AlertTitle,
14+
};

src/routes/(app)/404/+page.svelte

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
<script lang="ts">
2-
import { page } from '$app/state';
2+
import ErrorPageHandler from '$lib/components/error-page-handler.svelte';
33
</script>
44

5-
<main class="flex grow flex-col items-center justify-center">
6-
<h1 class="text-5xl font-bold">404</h1>
7-
<p class="text-xl">Page not found.</p>
8-
</main>
5+
<ErrorPageHandler error={'Not found'} status={404} />

src/routes/+error.svelte

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,35 @@
11
<script lang="ts">
2-
import { page } from '$app/state';
2+
import * as Sidebar from '$lib/components/ui/sidebar';
3+
import AppSidebar from '$lib/components/app-sidebar.svelte';
4+
import { socketCtx, UserInput } from '$lib/socket.svelte';
5+
import { appContext, type AppContext } from '$lib/context';
6+
import { CoralServer } from '$lib/CoralServer.svelte';
7+
import { onMount } from 'svelte';
8+
import ErrorPageHandler from '$lib/components/error-page-handler.svelte';
9+
10+
let ctx: AppContext = $state({
11+
server: new CoralServer(),
12+
connection: null,
13+
session: null,
14+
sessions: null,
15+
registry: null,
16+
logs: null
17+
});
18+
appContext.set(ctx);
19+
20+
onMount(() => {
21+
ctx.server.fetchAll();
22+
});
23+
24+
let socket = $state({
25+
userInput: new UserInput()
26+
});
27+
socketCtx.set(socket);
328
</script>
429

5-
<main class="flex grow flex-col items-center justify-center">
6-
<h1 class="text-5xl font-bold">{page.status}</h1>
7-
<p class="text-xl">{page.error?.message}</p>
8-
</main>
30+
<Sidebar.Provider>
31+
<AppSidebar />
32+
<Sidebar.Inset class="relative">
33+
<ErrorPageHandler />
34+
</Sidebar.Inset>
35+
</Sidebar.Provider>

0 commit comments

Comments
 (0)