feat: dashboard

This commit is contained in:
eric
2026-02-26 14:45:57 +01:00
parent 94ad2896cc
commit 3756830ec2
43 changed files with 7043 additions and 2060 deletions

View File

@@ -0,0 +1,125 @@
// oxlint-disable-next-line no-unused-vars
import { Html } from "@elysiajs/html";
import type { Guild, GuildDetailData, User } from "./shared";
import { GuildDetailView } from "./guild-detail";
export function DashboardEmptyState() {
return (
<div class="rounded-2xl border border-dashed border-slate-700 bg-slate-900/55 p-10 text-center">
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-xl border border-slate-700 bg-slate-800/70 text-xl">🛡</div>
<h2 class="text-2xl font-semibold text-white">Select a server</h2>
<p class="mx-auto mt-2 max-w-2xl text-sm text-slate-400">
Choose a server from the sidebar to configure Joel&apos;s system prompts and bot options.
</p>
</div>
);
}
export function DashboardSidebar({ user, guilds, initialGuild }: { user: User; guilds: Guild[]; initialGuild?: GuildDetailData }) {
return (
<aside class="h-fit rounded-2xl border border-slate-800 bg-slate-900/75 p-4 backdrop-blur-sm lg:sticky lg:top-6">
<div class="mb-4 flex items-center gap-3 rounded-xl border border-slate-800 bg-slate-950/90 px-3 py-3">
<div class="flex h-10 w-10 items-center justify-center rounded-xl bg-indigo-500/20 text-lg">🤖</div>
<div>
<div class="text-sm font-semibold text-white">Joel Dashboard</div>
<div class="text-xs text-slate-400">Server control panel</div>
</div>
</div>
<nav class="mb-4 space-y-1 rounded-xl border border-slate-800 bg-slate-950/60 p-2">
<button type="button" class="w-full rounded-lg bg-indigo-500/15 px-3 py-2 text-left text-sm font-medium text-indigo-200">
Servers
</button>
<button type="button" class="w-full rounded-lg px-3 py-2 text-left text-sm text-slate-500" disabled>
Settings
</button>
<button type="button" class="w-full rounded-lg px-3 py-2 text-left text-sm text-slate-500" disabled>
Analytics
</button>
<button type="button" class="w-full rounded-lg px-3 py-2 text-left text-sm text-slate-500" disabled>
Help
</button>
</nav>
<div class="rounded-xl border border-slate-800 bg-slate-950/60 p-3">
<div class="mb-2 flex items-center justify-between text-xs font-semibold uppercase tracking-wide text-slate-400">
<span>Your Servers</span>
<span>{guilds.length}</span>
</div>
<div id="guild-list" class="max-h-[52vh] space-y-2 overflow-y-auto pr-1">
{guilds.length === 0 ? (
<p class="text-sm text-slate-400">No shared servers found.</p>
) : (
guilds.map((g) => (
<button
type="button"
class={`guild-list-item flex w-full items-center gap-3 rounded-xl border px-3 py-2.5 text-left text-sm transition ${
initialGuild?.guildId === g.id
? "guild-item-active"
: "guild-item-inactive"
}`}
data-guild-id={g.id}
hx-get={`/dashboard/guild/${g.id}`}
hx-target="#guild-main-content"
hx-swap="innerHTML"
hx-push-url="true"
{...{ preload: "mouseover" }}
>
<span class="text-base">🛡</span>
<span class="truncate">{g.name}</span>
</button>
))
)}
</div>
</div>
<div class="mt-4 rounded-xl border border-slate-800 bg-slate-950/90 px-3 py-3">
<p class="text-[11px] font-medium uppercase tracking-wide text-slate-500">Logged in as</p>
<p class="mt-1 truncate text-sm text-slate-200">{user.global_name || user.username}</p>
</div>
</aside>
);
}
export function DashboardHeader() {
return (
<header class="flex flex-col gap-4 rounded-2xl border border-slate-800 bg-slate-900/75 px-4 py-4 backdrop-blur-sm sm:flex-row sm:items-center sm:justify-between sm:px-5">
<div>
<h1 class="text-lg font-semibold text-white sm:text-xl">Server Management</h1>
<p class="mt-1 text-sm text-slate-400">Configure Joel&apos;s prompts, behavior, and response settings.</p>
</div>
<div class="flex items-center gap-2">
<a
href="/ai-helper"
class="inline-flex items-center rounded-lg border border-indigo-500 bg-indigo-500/15 px-3 py-2 text-xs font-medium text-indigo-200 hover:bg-indigo-500/25"
>
🧠 AI Helper
</a>
<button
class="inline-flex items-center rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 text-xs font-medium text-slate-200 hover:bg-slate-700"
hx-post="/auth/logout"
hx-redirect="/"
>
Logout
</button>
</div>
</header>
);
}
export function DashboardMainContent({ initialGuild }: { initialGuild?: GuildDetailData }) {
return (
<section id="guild-main-content" class="space-y-4">
{initialGuild ? (
<GuildDetailView
guildId={initialGuild.guildId}
guildName={initialGuild.guildName}
options={initialGuild.options}
personalities={initialGuild.personalities}
/>
) : (
<DashboardEmptyState />
)}
</section>
);
}