feat: start dashboard

This commit is contained in:
sam 2024-10-18 22:13:23 +02:00
parent bacbc6db0e
commit ec7aa9faba
Signed by: sam
GPG key ID: 5F3C3C1B3166639D
50 changed files with 3624 additions and 18 deletions

View file

@ -0,0 +1,55 @@
export type User = {
id: string;
tag: string;
avatar_url: string;
};
export type PartialGuild = {
id: string;
name: string;
icon_url: string;
bot_in_guild: boolean;
};
export type CurrentUser = {
user: User;
guilds: PartialGuild[];
};
export type AuthCallback = CurrentUser & { token: string };
export type ApiError = {
error_code: string;
message: string;
};
export const TOKEN_KEY = "catalogger-token";
export default async function apiFetch<T>(
method: "GET" | "POST" | "PATCH" | "DELETE",
path: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
body: any = null,
) {
const token = localStorage.getItem(TOKEN_KEY);
const headers = {
...(body != null
? { "Content-Type": "application/json; charset=utf-8" }
: {}),
...(token ? { Authorization: token } : {}),
};
const reqBody = body ? JSON.stringify(body) : undefined;
console.debug("Sending", method, "request to", path, "with body", reqBody);
const resp = await fetch(path, {
method,
body: body ? JSON.stringify(body) : undefined,
headers,
});
if (resp.status < 200 || resp.status > 299)
throw (await resp.json()) as ApiError;
return (await resp.json()) as T;
}

View file

@ -0,0 +1,51 @@
<script lang="ts">
import { goto } from "$app/navigation";
import { TOKEN_KEY, type User } from "$lib/api";
import { addToast } from "$lib/toast";
import {
Button,
Navbar,
NavbarBrand,
NavbarToggler,
Collapse,
Nav,
NavItem,
NavLink,
} from "@sveltestrap/sveltestrap";
export let user: User | null;
let isOpen = false;
const logOut = async () => {
localStorage.removeItem(TOKEN_KEY);
addToast({ header: "Logged out", body: "Successfully logged out." });
await goto("/", { invalidateAll: true });
};
</script>
<Navbar expand="lg">
<NavbarBrand href="/">Catalogger</NavbarBrand>
<NavbarToggler on:click={() => (isOpen = !isOpen)} />
<Collapse {isOpen} navbar expand="lg">
<Nav class="ms-auto" navbar>
<NavItem>
<NavLink href="/">Home</NavLink>
</NavItem>
{#if user}
<NavItem>
<NavLink href="/dash">Dashboard</NavLink>
</NavItem>
<NavItem>
<NavLink on:click={logOut}>Log out</NavLink>
</NavItem>
{:else}
<NavItem>
<NavLink href="/api/authorize">Log in with Discord</NavLink>
</NavItem>
{/if}
</Nav>
</Collapse>
</Navbar>

View file

@ -0,0 +1 @@
// place files you want to import through the `$lib` alias in this folder.

View file

@ -0,0 +1,37 @@
import { writable } from "svelte/store";
export interface ToastData {
header?: string;
body: string;
duration?: number;
}
interface IdToastData extends ToastData {
id: number;
}
export const toastStore = writable<IdToastData[]>([]);
let maxId = 0;
export const addToast = (data: ToastData) => {
const id = maxId++;
toastStore.update((toasts) => (toasts = [...toasts, { ...data, id }]));
if (data.duration !== -1) {
setTimeout(() => {
toastStore.update(
(toasts) => (toasts = toasts.filter((toast) => toast.id !== id)),
);
}, data.duration ?? 5000);
}
return id;
};
export const delToast = (id: number) => {
toastStore.update(
(toasts) => (toasts = toasts.filter((toast) => toast.id !== id)),
);
};