1import { Route, Router, type RouteSectionProps, useLocation } from "@solidjs/router";
2import Eye from "lucide-solid/icons/eye";
3import { createResource, Show } from "solid-js";
4import { render } from "solid-js/web";
5import { parseResponse } from "hono/client";
6import { api, queries } from "./api.ts";
7import { Loaded } from "./components/Loaded.tsx";
8import { PAGES, Sidebar, viewAs } from "./components/Sidebar.tsx";
9import { Toasts } from "./components/Toast.tsx";
10import { Tooltips } from "./components/Tooltip.tsx";
11import { Overview } from "./pages/Overview.tsx";
12import { Service } from "./pages/Service.tsx";
13import { Storage } from "./pages/Storage.tsx";
14import { Media } from "./pages/Media.tsx";
15import { Seedbox } from "./pages/Seedbox.tsx";
16import { YouTube } from "./pages/YouTube.tsx";
17import { PaperClover } from "./pages/PaperClover.tsx";
18import { directory, Users } from "./pages/Users.tsx";
19import { Deploys } from "./pages/Deploys.tsx";
20import { Deploy } from "./pages/Deploy.tsx";
21import { VMs } from "./pages/VMs.tsx";
22import { MCP } from "./pages/MCP.tsx";
23import { Account } from "./pages/Account.tsx";
24import "./styles.css";
25
26const [me, { refetch }] = createResource(() => parseResponse(api.me.$get()));
27
28/** A route preload, which the router also runs on link hover, starting each query's first load. */
29const preload = (...list: { preload(): void }[]) => () => list.forEach((query) => query.preload());
30
31function Shell(props: RouteSectionProps) {
32 const location = useLocation();
33 const section = () => location.pathname.startsWith("/services/") ? "admin"
34 : PAGES.find((page) => page.href !== "/" && location.pathname.startsWith(page.href))?.section;
35 return (
36 <>
37 <Loaded data={me} what="your account" retry={refetch} skeleton={<div class="shell"><div class="sidebar" /><main class="main" /></div>}>
38 {(user) => (
39 <div class="shell">
40 <Show when={user().viewing}>
41 <div class="viewing-as" role="status">
42 <Eye size={15} />
43 <span>previewing as <b>{user().groups.length ? `someone in ${user().groups.join(", ")}` : "a friend in no groups"}</b></span>
44 <button class="button small" onClick={() => viewAs(null)}>back to admin</button>
45 </div>
46 </Show>
47 <Sidebar me={user()} />
48 <main class="main">
49 <Show when={!section() || user().sections.includes(section()!)} fallback={
50 <div class="page empty">Your account can't open this page. Ask an admin to add you to its group.</div>
51 }>
52 {props.children}
53 </Show>
54 </main>
55 </div>
56 )}
57 </Loaded>
58 <Tooltips />
59 <Toasts />
60 </>
61 );
62}
63
64render(() => (
65 <Router root={Shell}>
66 <Route path="/" component={() => <Overview me={me.latest!} />} preload={() => {
67 queries.launcher.preload();
68 if (me.latest?.sections.includes("metrics")) preload(queries.host, queries.storage, queries.services)();
69 }} />
70 <Route path="/services/:id/:tab?" component={Service} preload={({ params }) => {
71 queries.service.preload(params.id!);
72 queries.deploys.preload();
73 }} />
74 <Route path="/storage" component={Storage} preload={preload(queries.storage, queries.launcher)} />
75 <Route path="/media" component={Media} preload={preload(queries.launcher)} />
76 <Route path="/seedbox" component={Seedbox} preload={preload(queries.seedbox, queries.seedboxHistory, queries.launcher)} />
77 <Route path="/youtube" component={YouTube} />
78 <Route path="/paper-clover" component={PaperClover} preload={preload(queries.paperClover, queries.paperCloverActivity)} />
79 <Route path="/users/:name?" component={Users} preload={preload(directory)} />
80 <Route path="/deploys" component={Deploys} preload={preload(queries.deploys, queries.services)} />
81 <Route path="/deploys/:id/:tab?" component={Deploy} preload={preload(queries.deploys, queries.services)} />
82 <Route path="/vms" component={VMs} preload={preload(queries.vms)} />
83 <Route path="/mcp" component={MCP} />
84 <Route path="/account" component={Account} preload={preload(queries.launcher)} />
85 <Route path="*" component={() => <div class="empty">No page here</div>} />
86 </Router>
87), document.getElementById("root")!);