Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 5 additions & 12 deletions web/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,13 @@ interface FetchOptions extends RequestInit {
export async function fetchApi(endpoint: string, options: FetchOptions = {}): Promise<Response> {
const { params = {}, token, ...fetchOptions } = options;

const url = new URL(`${window.location.origin}/api${endpoint}`);
Object.keys(params).forEach((key) => url.searchParams.append(key, params[key]));
const base = `/api${endpoint}`;
const query = new URLSearchParams(params).toString();
const url = query ? `${base}${base.includes("?") ? "&" : "?"}${query}` : base;

const init = {
headers: {
...fetchOptions.headers,
},
...fetchOptions,
};
const init: RequestInit = { ...fetchOptions };
if (token) {
init.headers = {
...init.headers,
Authorization: `Basic ${token}`,
};
init.headers = { ...init.headers, Authorization: `Basic ${token}` };
}
return await fetch(url, init);
}
26 changes: 12 additions & 14 deletions web/src/lib/components/LabeledCheckbox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,17 @@
</script>

<Checkbox.Root id={checkboxId} aria-labelledby={labelId} bind:checked class="flex cursor-pointer items-center justify-between gap-2 btn-ghost px-2 py-1">
{#snippet children({ checked })}
<Label.Root id={labelId} for={checkboxId} class="cursor-pointer">
{labelText}
</Label.Root>
<div
class="relative size-5 rounded-sm border bg-neutral transition-colors ease-in-out data-[state=checked]:bg-primary"
<Label.Root id={labelId} for={checkboxId} class="cursor-pointer">
{labelText}
</Label.Root>
<div
class="relative size-5 rounded-sm border bg-neutral transition-colors ease-in-out data-[state=checked]:bg-primary"
data-state={checked ? "checked" : "unchecked"}
>
<span
class="absolute top-1/2 left-1/2 iconify size-4 -translate-x-1/2 -translate-y-1/2 bg-white opacity-0 transition-opacity ease-in-out octicon--check-16 data-[state=checked]:opacity-100"
aria-hidden="true"
data-state={checked ? "checked" : "unchecked"}
>
<span
class="absolute top-1/2 left-1/2 iconify size-4 -translate-x-1/2 -translate-y-1/2 bg-white opacity-0 transition-opacity ease-in-out octicon--check-16 data-[state=checked]:opacity-100"
aria-hidden="true"
data-state={checked ? "checked" : "unchecked"}
></span>
</div>
{/snippet}
></span>
</div>
</Checkbox.Root>
13 changes: 10 additions & 3 deletions web/src/lib/components/PatchesStats.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
</script>

<div class="flex w-full flex-1 flex-col">
{#if !data?.total}
{#if !data}
<div class="flex h-full flex-col items-center justify-center">
<span class="mb-2">Loading statistics...</span>
<Spinner />
Expand Down Expand Up @@ -111,8 +111,15 @@
Time Spent: {Duration.fromISO(data.timeSpent).toHuman()}
</span>
</div>
<div class="h-4 w-full overflow-hidden rounded-full shadow">
<div class="flex h-full w-full bg-gray-200 dark:dark:bg-white/20">
<div
class="h-4 w-full overflow-hidden rounded-full shadow"
role="progressbar"
aria-label="Patch progress"
aria-valuemin={0}
aria-valuemax={data.total}
aria-valuenow={data.done}
>
<div class="flex h-full w-full bg-gray-200 dark:bg-white/20">
<div class="h-full bg-green-500 dark:bg-green-800" style="width: {getProgressPercentage(data.done, data.total)}%"></div>
<div class="h-full bg-orange-500 dark:bg-orange-800" style="width: {getProgressPercentage(data.wip, data.total)}%"></div>
<div class="h-full bg-yellow-500 dark:bg-yellow-800" style="width: {getProgressPercentage(data.available, data.total)}%"></div>
Expand Down
3 changes: 0 additions & 3 deletions web/src/lib/components/PatchesTable.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,3 @@
<div class="flex w-full flex-1">
<AgGrid {gridOptions} rowData={data} {modules} {gridClass} />
</div>

<style>
</style>
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
<script module>
</script>

<script lang="ts">
import { useId, Label } from "bits-ui";
import type { Snippet } from "svelte";
Expand All @@ -12,17 +9,11 @@

let { children, title }: Props = $props();

let groupId = useId();
let labelId = useId();
const groupId = useId();
const labelId = useId();
</script>

{#snippet renderChildren()}
{#if children}
{@render children()}
{/if}
{/snippet}

<div id={groupId} aria-labelledby={labelId} class="flex flex-col" role="group">
<Label.Root id={labelId} for={groupId} class="px-2 pt-4 pb-1 font-semibold">{title}</Label.Root>
{@render renderChildren()}
{@render children?.()}
</div>
8 changes: 4 additions & 4 deletions web/src/lib/index.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ export class PatchRouletteState {
return;
}

const patchResponse = await fetchApi(`/get-all-patches?minecraftVersion=${mcVersion}`, {
const patchResponse = await fetchApi(`/get-all-patches?minecraftVersion=${encodeURIComponent(mcVersion)}`, {
method: "GET",
token: localStorage.getItem("token")!,
token: token.value ?? undefined,
});

if (patchResponse.ok) {
Expand All @@ -64,9 +64,9 @@ export class PatchRouletteState {
alert("Failed to fetch patches. Please try again.");
}

const statsResponse = await fetchApi(`/stats?minecraftVersion=${mcVersion}`, {
const statsResponse = await fetchApi(`/stats?minecraftVersion=${encodeURIComponent(mcVersion)}`, {
method: "GET",
token: localStorage.getItem("token")!,
token: token.value ?? undefined,
});

if (statsResponse.ok) {
Expand Down
1 change: 1 addition & 0 deletions web/src/lib/theme.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ function initialTheme() {
return "auto";
}

// Module state is safe here only because the app is fully prerendered; revisit if SSR is introduced.
let theme: Theme = $state(initialTheme());

const prefersDark = new MediaQuery("prefers-color-scheme: dark");
Expand Down
15 changes: 4 additions & 11 deletions web/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,15 @@
}
}

async function handleVersionSelect(event: Event) {
instance.selectedVersion = (event.target as HTMLSelectElement).value;
if (instance.selectedVersion === "" || instance.selectedVersion === null) {
return;
} else if (instance.selectedVersion) {
await instance.onVersionSelect((event.target as HTMLSelectElement).value);
function handleVersionSelect() {
if (instance.selectedVersion) {
instance.onVersionSelect(instance.selectedVersion);
}
}

onMount(async () => {
token.value = localStorage.getItem("token");

if (token.value === null) {
await goto(resolve("/login"));
return;
}

await loadMinecraftVersions();

if (minecraftVersions.length > 0) {
Expand Down Expand Up @@ -92,6 +84,7 @@
<button
class="flex items-center justify-center rounded-sm btn-ghost px-2 py-1 text-primary data-[active=true]:btn-ghost-visible"
data-active={currentView === view}
aria-pressed={currentView === view}
onclick={() => (currentView = view)}
>
{capitalizeFirstLetter(view)}
Expand Down
9 changes: 9 additions & 0 deletions web/src/routes/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { browser } from "$app/environment";
import { resolve } from "$app/paths";
import { redirect } from "@sveltejs/kit";

export function load() {
if (browser && localStorage.getItem("token") === null) {
redirect(307, resolve("/login"));
}
}
8 changes: 0 additions & 8 deletions web/src/routes/login/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
<script lang="ts">
import Login from "./Login.svelte";
import { onMount } from "svelte";
import { goto } from "$app/navigation";
import { resolve } from "$app/paths";

onMount(() => {
const token = localStorage.getItem("token");
if (token) {
onLogin();
}
});

function onLogin() {
goto(resolve("/"));
}
Expand Down
9 changes: 9 additions & 0 deletions web/src/routes/login/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { browser } from "$app/environment";
import { resolve } from "$app/paths";
import { redirect } from "@sveltejs/kit";

export function load() {
if (browser && localStorage.getItem("token") !== null) {
redirect(307, resolve("/"));
}
}
2 changes: 2 additions & 0 deletions web/src/routes/login/Login.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
type="text"
id="username"
name="username"
autocomplete="username"
class="w-full rounded border px-3 py-2 focus:ring-2 focus:ring-primary focus:outline-none"
bind:value={username}
/>
Expand All @@ -55,6 +56,7 @@
type="password"
id="password"
name="password"
autocomplete="current-password"
class="w-full rounded border px-3 py-2 focus:ring-2 focus:ring-primary focus:outline-none"
bind:value={password}
/>
Expand Down