Files
xevion.dev/web/src/routes/admin/+page.svelte
Xevion 462b510e14 feat: add icon picker for tags with Iconify integration
- Add icon field to tag creation/update API and handlers
- Install @iconify packages (json, types, utils) as production deps
- Build IconPicker component for tag admin UI
- Fix apiFetch lazy initialization for build-time safety
- Update Docker to install production dependencies for SSR runtime
2026-01-07 20:57:12 -06:00

87 lines
2.4 KiB
Svelte

<script lang="ts">
import { resolve } from "$app/paths";
import Button from "$lib/components/admin/Button.svelte";
import EventLog from "$lib/components/admin/EventLog.svelte";
import { getAdminEvents } from "$lib/api";
import type { AdminEvent } from "$lib/admin-types";
import IconPlus from "~icons/lucide/plus";
let recentEvents = $state<AdminEvent[]>([]);
let loading = $state(true);
async function loadDashboard() {
try {
const eventsData = await getAdminEvents();
recentEvents = eventsData;
} catch (error) {
console.error("Failed to load dashboard:", error);
} finally {
loading = false;
}
}
$effect(() => {
loadDashboard();
});
</script>
<svelte:head>
<title>Dashboard | Admin</title>
</svelte:head>
<div class="space-y-6">
<!-- Header -->
<div>
<h1 class="text-xl font-semibold text-admin-text">Dashboard</h1>
<p class="mt-1 text-sm text-admin-text-muted">
Overview of your portfolio and recent activity
</p>
</div>
{#if loading}
<div class="text-center py-12 text-admin-text-muted">
Loading dashboard...
</div>
{:else}
<!-- Quick Actions -->
<div class="flex flex-wrap gap-3">
<Button variant="primary" href="/admin/projects/new">
<IconPlus class="w-4 h-4 mr-2" />
New Project
</Button>
<Button variant="secondary" href="/admin/projects">
View All Projects
</Button>
<Button variant="secondary" href="/admin/tags">Manage Tags</Button>
<Button variant="secondary" href="/admin/events">View Events</Button>
</div>
<!-- Recent Events -->
<div
class="rounded-xl border border-admin-border bg-admin-surface overflow-hidden shadow-sm shadow-black/10 dark:shadow-black/20"
>
<div
class="flex items-center justify-between px-6 py-3.5 bg-admin-surface-hover border-b border-admin-border"
>
<h2 class="text-sm font-medium text-admin-text-secondary">
Recent Events
</h2>
<a
href={resolve("/admin/events")}
class="text-sm text-admin-accent hover:text-admin-accent-hover transition-colors"
>
View all →
</a>
</div>
{#if recentEvents.length === 0}
<p class="text-sm text-admin-text-muted text-center py-8">
No events yet
</p>
{:else}
<EventLog events={recentEvents} maxHeight="400px" />
{/if}
</div>
{/if}
</div>