Files
website/src/routes/projects/progress/[id]/+page.svelte
Stan Runge 54924b39f0
Some checks failed
Test and deploy staging / staging (push) Failing after 43s
feat: upgrade to SvelteKit 3 and adapter-bun
Run the sveltekit-3 migration and finish its manual tasks:

- Move kit config into the sveltekit() Vite plugin; drop svelte.config.js
- Replace $lib with #lib subpath imports
- Declare env vars in src/env.ts and import them from $app/env/{private,public}
- Consolidate param matchers into src/params.ts
- Use error(status, message), Response.json, refreshAll and goto reset/shallow
- Fall back to a full page load when goto rejects a login return path

Switch from adapter-node to adapter-bun: the build script runs Vite under
Bun (the adapter requires it) and the image starts with `bun ./build`.
Runtime env names (PORT, BODY_SIZE_LIMIT, ADDRESS_HEADER, ...) are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:17:30 +00:00

124 lines
4.4 KiB
Svelte
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts">
import { NativeSelect } from '#lib/components/ui/native-select/index.js';
import { resolve } from '$app/paths';
import { Button } from '#lib/components/ui/button/index.js';
import { Badge } from '#lib/components/ui/badge/index.js';
import { Input } from '#lib/components/ui/input/index.js';
import { Label } from '#lib/components/ui/label/index.js';
import { Textarea } from '#lib/components/ui/textarea/index.js';
import * as Card from '#lib/components/ui/card/index.js';
import ProgressBar from '#lib/components/ProgressBar.svelte';
import type { PageProps } from './$types';
let { data }: PageProps = $props();
</script>
<div class="mx-auto max-w-4xl space-y-6 px-4 py-12 sm:px-6">
<Button href={resolve('projects/progress')} variant="ghost" size="sm">← All tasks</Button>
{#if data.task}
<header>
<h1 class="text-4xl font-semibold tracking-tight">{data.task.name}</h1>
<p class="mt-2 text-muted-foreground">
{data.task.currentPoints}/{data.task.totalPoints} points
</p>
</header>
<ProgressBar
value={data.task.currentPoints}
max={data.task.totalPoints}
label={`Progress on ${data.task.name}`}
/>
<Card.Root
><Card.Header><Card.Title>Topics</Card.Title></Card.Header><Card.Content class="space-y-4">
<div class="flex flex-wrap gap-2">
{#each data.task.tasksToTopics as relation (relation.topicId)}
{#if relation.topic}
<div class="flex items-center gap-1">
<a
href={resolve('/projects/progress/topics/[id]', {
id: String(relation.topic.id)
})}><Badge variant="secondary">{relation.topic.name}</Badge></a
>
{#if data.editable}<form method="POST" action="?/removeTopic">
<input type="hidden" name="topic-id" value={relation.topic.id} /><Button
type="submit"
variant="ghost"
size="icon-xs"
aria-label={`Remove ${relation.topic.name}`}>×</Button
>
</form>{/if}
</div>
{/if}
{/each}
</div>
{#if data.editable}
<form method="POST" action="?/addTopic" class="flex flex-wrap items-end gap-2">
<div class="min-w-48 flex-1 space-y-2">
<Label for="topic-id">Add existing topic</Label><NativeSelect
id="topic-id"
name="topic-id"
required
class="h-9 w-full rounded-md border border-input bg-background px-2.5 text-sm"
><option value="">Choose a topic</option
>{#each data.topics as topic (topic.id)}<option value={topic.id}
>{topic.name}</option
>{/each}</NativeSelect
>
</div>
<Button type="submit" variant="outline">Add</Button>
</form>
<form method="POST" action="?/createTopic" class="flex flex-wrap items-end gap-2">
<div class="min-w-48 flex-1 space-y-2">
<Label for="new-topic">New topic</Label><Input id="new-topic" name="name" required />
</div>
<Button type="submit" variant="outline">Create</Button>
</form>
{/if}
</Card.Content></Card.Root
>
<Card.Root
><Card.Header><Card.Title>Progress</Card.Title></Card.Header><Card.Content>
<form method="POST" action="?/updateProgress" class="flex flex-wrap items-end gap-3">
<div class="space-y-2">
<Label for="current-points">Current</Label><Input
id="current-points"
type="number"
name="currentPoints"
min={0}
value={data.task.currentPoints}
readonly={!data.editable}
class="w-28"
/>
</div>
<div class="space-y-2">
<Label for="total-points">Total</Label><Input
id="total-points"
type="number"
name="totalPoints"
min={1}
value={data.task.totalPoints}
readonly={!data.editable}
class="w-28"
/>
</div>
{#if data.editable}<Button type="submit">Save progress</Button>{/if}
</form>
</Card.Content></Card.Root
>
<Card.Root
><Card.Header><Card.Title>Notes</Card.Title></Card.Header><Card.Content
><form method="POST" action="?/updateNotes" class="space-y-3">
<Label for="task-notes" class="sr-only">Notes</Label><Textarea
id="task-notes"
name="notes"
value={data.task.notes ?? ''}
readonly={!data.editable}
class="min-h-36"
/>{#if data.editable}<Button type="submit">Save notes</Button>{/if}
</form></Card.Content
></Card.Root
>
{:else}
<p class="text-muted-foreground">Task not found.</p>
{/if}
</div>