diff --git a/app/components/Header.tsx b/app/components/Header.tsx new file mode 100644 index 0000000..f31f576 --- /dev/null +++ b/app/components/Header.tsx @@ -0,0 +1,109 @@ +import { GearIcon, GlobeIcon, LockIcon, PaperAirplaneIcon, PeopleIcon, PersonIcon, ServerIcon } from '@primer/octicons-react' +import { Form } from '@remix-run/react' + +import { cn } from '~/utils/cn' +import { type Context } from '~/utils/config' +import { type SessionData } from '~/utils/sessions' + +import Menu from './Menu' +import TabLink from './TabLink' + +type Properties = { + readonly data?: Context & { user?: SessionData['user'] }; +} + +type LinkProperties = { + readonly href: string; + readonly text: string; + readonly isMenu?: boolean; +} + +function Link({ href, text, isMenu }: LinkProperties) { + return ( + + {text} + + ) +} + +export default function Header({ data }: Properties) { + return ( + + + + + Headplane + + + + + + {data?.user ? ( + + + + + + + {data.user.name} + {data.user.email} + + + + + + + + + + + + + + Logout + + + + + + ) : undefined} + + + + }/> + }/> + {data?.hasAcl ? }/> : undefined} + {data?.hasConfig ? ( + <> + }/> + }/> + > + ) : undefined} + + + ) +} diff --git a/app/components/TabLink.tsx b/app/components/TabLink.tsx index 935547c..2b31284 100644 --- a/app/components/TabLink.tsx +++ b/app/components/TabLink.tsx @@ -13,8 +13,9 @@ export default function TabLink({ name, to, icon }: Properties) { clsx( - 'flex items-center gap-x-2 p-2 border-b-2 text-md', - isActive ? 'border-white' : 'border-transparent' + 'flex items-center gap-x-2 p-2 border-b-2 text-md text-nowrap', + isActive ? 'border-white' : 'border-transparent', + isPending && 'animate-pulse' )} > {icon} {name} diff --git a/app/routes/_data.tsx b/app/routes/_data.tsx index 373bc9f..7d5dd92 100644 --- a/app/routes/_data.tsx +++ b/app/routes/_data.tsx @@ -1,10 +1,8 @@ -import { Cog8ToothIcon, CpuChipIcon, GlobeAltIcon, LockClosedIcon, ServerStackIcon, UserCircleIcon, UsersIcon } from '@heroicons/react/24/outline' import { type LoaderFunctionArgs, redirect } from '@remix-run/node' -import { Form, Outlet, useLoaderData } from '@remix-run/react' +import { Outlet, useLoaderData } from '@remix-run/react' import { ErrorPopup } from '~/components/Error' -import Menu from '~/components/Menu' -import TabLink from '~/components/TabLink' +import Header from '~/components/Header' import { getContext } from '~/utils/config' import { HeadscaleError, pull } from '~/utils/headscale' import { destroySession, getSession } from '~/utils/sessions' @@ -20,7 +18,6 @@ export async function loader({ request }: LoaderFunctionArgs) { await pull('v1/apikey', session.get('hsApiKey')!) } catch (error) { if (error instanceof HeadscaleError) { - console.error(error) // Safest to just redirect to login if we can't pull return redirect('/login', { headers: { @@ -46,58 +43,9 @@ export default function Layout() { return ( <> - - - - - - Headplane - - - - Download - - - GitHub - - - Headscale - - - - - - - - {data.user?.name} - {data.user?.email} - - - - - Logout - - - - - - - - - }/> - }/> - {data.hasAcl ? }/> : undefined} - {data.hasConfig ? ( - <> - }/> - }/> - > - ) : undefined} - - - + - + > @@ -107,21 +55,7 @@ export default function Layout() { export function ErrorBoundary() { return ( <> - - - - - Headplane - - - }/> - }/> - }/> - }/> - }/> - - - + > ) diff --git a/app/utils/config.ts b/app/utils/config.ts index 341dbc7..bbc8093 100644 --- a/app/utils/config.ts +++ b/app/utils/config.ts @@ -196,7 +196,7 @@ export function registerConfigWatcher() { }) } -type Context = { +export type Context = { hasDockerSock: boolean; hasConfig: boolean; hasConfigWrite: boolean; diff --git a/app/utils/sessions.ts b/app/utils/sessions.ts index 1e9f087..39d93c3 100644 --- a/app/utils/sessions.ts +++ b/app/utils/sessions.ts @@ -1,6 +1,6 @@ import { createCookieSessionStorage } from '@remix-run/node' // Or cloudflare/deno -type SessionData = { +export type SessionData = { hsApiKey: string; authState: string; authNonce: string;
{data.user.name}
{data.user.email}
{data.user?.name}
{data.user?.email}