83 lines
3.2 KiB
TypeScript
83 lines
3.2 KiB
TypeScript
|
|
import { createSignal, createResource, Show, For } from 'solid-js';
|
||
|
|
import { A, useSearchParams } from '@solidjs/router';
|
||
|
|
import ProfileWidget from '~/components/dashboard/ProfileWidget';
|
||
|
|
|
||
|
|
async function fetchRuntimeConfig(roleKey: string) {
|
||
|
|
const RUST_API_URL = import.meta.env.VITE_RUST_API_URL || 'http://localhost:8080';
|
||
|
|
|
||
|
|
// Try to lookup Role ID first
|
||
|
|
const roleRes = await fetch(`${RUST_API_URL}/api/admin/roles/${roleKey}`);
|
||
|
|
if (!roleRes.ok) throw new Error("Role not found");
|
||
|
|
const role = await roleRes.json();
|
||
|
|
|
||
|
|
// Then fetch Dashboard config for that role
|
||
|
|
const configRes = await fetch(`${RUST_API_URL}/api/admin/dashboard-config/${role.id}?audience=EXTERNAL`);
|
||
|
|
if (!configRes.ok) throw new Error("Dashboard config not found");
|
||
|
|
const dashboardConfig = await configRes.json();
|
||
|
|
|
||
|
|
return dashboardConfig.config_json; // Returns `{ sidebar: [...], widgets: [...] }`
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function WorkspaceLayout(props: { children?: any }) {
|
||
|
|
const [searchParams] = useSearchParams();
|
||
|
|
const rawRoleKey = searchParams.roleKey;
|
||
|
|
const roleKey = () => (Array.isArray(rawRoleKey) ? rawRoleKey[0] : rawRoleKey) || 'PHOTOGRAPHER';
|
||
|
|
|
||
|
|
const [config] = createResource<any, string>(roleKey, fetchRuntimeConfig);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div class="min-h-screen bg-slate-50 flex">
|
||
|
|
{/* Sidebar rendered magically from Rust Config */}
|
||
|
|
<aside class="w-64 bg-white border-r border-slate-200 flex flex-col">
|
||
|
|
<div class="p-6 border-b border-slate-200">
|
||
|
|
<h1 class="font-bold text-xl text-orange-600">Nxtgauge Workspace</h1>
|
||
|
|
<p class="text-xs text-slate-500 mt-1 capitalize">{roleKey().toLowerCase()}</p>
|
||
|
|
</div>
|
||
|
|
<nav class="flex-1 p-4 space-y-1">
|
||
|
|
<Show when={config.loading}>
|
||
|
|
<p class="text-sm text-slate-500">Loading modules...</p>
|
||
|
|
</Show>
|
||
|
|
<Show when={config.error}>
|
||
|
|
<p class="text-sm text-red-500">Failed to load shell config.</p>
|
||
|
|
</Show>
|
||
|
|
<Show when={config()}>
|
||
|
|
<For each={config().sidebar}>
|
||
|
|
{(item: any) => (
|
||
|
|
<A
|
||
|
|
href={item.route}
|
||
|
|
class="block px-4 py-2 rounded-lg text-slate-700 hover:bg-orange-50 hover:text-orange-600 transition-colors"
|
||
|
|
>
|
||
|
|
{item.label}
|
||
|
|
</A>
|
||
|
|
)}
|
||
|
|
</For>
|
||
|
|
</Show>
|
||
|
|
</nav>
|
||
|
|
</aside>
|
||
|
|
|
||
|
|
{/* Main Content Area */}
|
||
|
|
<main class="flex-1 p-8">
|
||
|
|
<Show when={config() && config().widgets}>
|
||
|
|
<div class="grid grid-cols-2 gap-6 mb-8">
|
||
|
|
<For each={config().widgets}>
|
||
|
|
{(widget: any) => (
|
||
|
|
<Show when={widget.enabled}>
|
||
|
|
<div class="bg-white p-6 rounded-xl border border-slate-200 shadow-sm">
|
||
|
|
<h3 class="font-medium text-slate-800">{widget.title}</h3>
|
||
|
|
<p class="text-xs text-slate-400 mt-2">Dynamic Widget Module</p>
|
||
|
|
</div>
|
||
|
|
</Show>
|
||
|
|
)}
|
||
|
|
</For>
|
||
|
|
</div>
|
||
|
|
</Show>
|
||
|
|
|
||
|
|
{/* Profile Settings specifically for this Role */}
|
||
|
|
<ProfileWidget roleKey={roleKey()} />
|
||
|
|
|
||
|
|
{props.children}
|
||
|
|
</main>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|