feat(ui): implement OAuth login flow and dashboard components
CI / build-and-test (push) Failing after -32m0s

* Add OAuth login handling in app and UI components
* Create new components for login and dashboard pages
* Refactor sidebar and navigation structure
* Introduce types for access entries and status responses
This commit is contained in:
2026-04-26 09:12:46 +02:00
parent bdc78cc2a3
commit 71845d38d3
22 changed files with 1440 additions and 334 deletions
@@ -0,0 +1,91 @@
<script lang="ts">
import { onMount } from 'svelte';
import { api } from '../../api';
import type { AgentSkill } from '../../types';
let skills = $state<AgentSkill[]>([]);
let loading = $state(true);
let error = $state('');
let busy = $state<string | null>(null);
async function load() {
loading = true;
error = '';
try {
skills = await api.skills.list();
} catch (e) {
error = e instanceof Error ? e.message : 'Failed to load skills';
} finally {
loading = false;
}
}
async function remove(id: string, name: string) {
if (!confirm(`Delete skill "${name}"?`)) return;
busy = id;
try {
await api.skills.delete(id);
await load();
} catch (e) {
error = e instanceof Error ? e.message : 'Delete failed';
} finally {
busy = null;
}
}
onMount(load);
</script>
<div class="space-y-4">
<div class="flex items-end justify-between">
<div>
<h2 class="text-2xl font-semibold text-white">Skills</h2>
<p class="mt-1 text-sm text-slate-400">{skills.length} skill{skills.length !== 1 ? 's' : ''}</p>
</div>
<button
class="rounded-xl border border-white/10 bg-white/5 px-4 py-2 text-sm text-slate-200 transition hover:bg-white/10"
onclick={load}
>Refresh</button>
</div>
{#if error}
<div class="rounded-2xl border border-rose-400/30 bg-rose-400/10 px-4 py-4 text-sm text-rose-100">{error}</div>
{/if}
{#if loading}
<div class="rounded-2xl border border-dashed border-white/10 bg-slate-950/40 py-12 text-center text-slate-400">Loading…</div>
{:else if skills.length === 0}
<div class="rounded-2xl border border-dashed border-white/10 bg-slate-950/40 py-12 text-center text-slate-500">No skills registered.</div>
{:else}
<div class="space-y-3">
{#each skills as skill}
<div class="rounded-2xl border border-white/10 bg-white/5 p-5">
<div class="flex items-start justify-between gap-4">
<div class="min-w-0">
<p class="font-semibold text-white">{skill.name}</p>
{#if skill.description}
<p class="mt-1 text-sm text-slate-400">{skill.description}</p>
{/if}
{#if skill.tags?.length}
<div class="mt-2 flex flex-wrap gap-1">
{#each skill.tags as tag}
<span class="rounded-full border border-white/10 bg-white/5 px-2 py-0.5 text-xs text-slate-400">{tag}</span>
{/each}
</div>
{/if}
</div>
<button
class="shrink-0 text-xs text-rose-400 hover:text-rose-300 disabled:opacity-40"
onclick={() => remove(skill.id, skill.name)}
disabled={busy === skill.id}
>Delete</button>
</div>
<details class="mt-3">
<summary class="cursor-pointer text-xs text-slate-500 hover:text-slate-300">View content</summary>
<pre class="mt-2 overflow-x-auto rounded-xl bg-slate-950/60 p-3 text-xs text-slate-300 whitespace-pre-wrap">{skill.content}</pre>
</details>
</div>
{/each}
</div>
{/if}
</div>