feat: add user page

This commit is contained in:
Aarnav Tale 2024-03-26 10:57:11 -04:00
parent 22c4b9504b
commit c62da81ea7
No known key found for this signature in database
3 changed files with 70 additions and 3 deletions

View File

@ -10,8 +10,8 @@ type Properties = {
export default function Attribute({ name, value, isCopyable }: Properties) {
const canCopy = isCopyable ?? false
return (
<dl className='flex gap-1 text-sm'>
<dt className='w-1/4 shrink-0 min-w-0 truncate text-gray-700 dark:text-gray-300'>
<dl className='flex gap-1 text-sm w-full'>
<dt className='w-1/4 shrink-0 min-w-0 truncate text-gray-700 dark:text-gray-300 py-1'>
{name}
</dt>

View File

@ -1,4 +1,4 @@
import { CpuChipIcon, ServerStackIcon } from '@heroicons/react/24/outline'
import { CpuChipIcon, ServerStackIcon, UsersIcon } from '@heroicons/react/24/outline'
import { type LoaderFunctionArgs, redirect } from '@remix-run/node'
import { Outlet } from '@remix-run/react'
@ -46,6 +46,7 @@ export default function Layout() {
</div>
<div className='flex items-center gap-x-4'>
<TabLink to='/machines' name='Machines' icon={<ServerStackIcon className='w-4 h-4'/>}/>
<TabLink to='/users' name='Users' icon={<UsersIcon className='w-4 h-4'/>}/>
</div>
</nav>
</header>

View File

@ -0,0 +1,66 @@
/* eslint-disable unicorn/filename-case */
import { ClipboardIcon, UserIcon } from '@heroicons/react/24/outline'
import { type LoaderFunctionArgs } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import { toast } from 'react-hot-toast/headless'
import Attribute from '~/components/Attribute'
import StatusCircle from '~/components/StatusCircle'
import { type Machine, type User } from '~/types'
import { pull } from '~/utils/headscale'
import { getSession } from '~/utils/sessions'
import { useLiveData } from '~/utils/useLiveData'
export async function loader({ request }: LoaderFunctionArgs) {
const session = await getSession(request.headers.get('Cookie'))
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const data = await pull<{ nodes: Machine[] }>('v1/node', session.get('hsApiKey')!)
const users = new Map<string, Machine[]>()
for (const machine of data.nodes) {
const { user } = machine
if (!users.has(user.id)) {
users.set(user.id, [])
}
users.get(user.id)?.push(machine)
}
return [...users.values()].map(machines => {
const { user } = machines[0]
return {
...user,
machines
}
})
}
export default function Page() {
const data = useLoaderData<typeof loader>()
useLiveData({ interval: 3000 })
return (
<div className='grid grid-cols-2 gap-4 auto-rows-min'>
{data.map(user => (
<div key={user.id} className='border rounded-lg p-4'>
<div className='flex items-center gap-4'>
<UserIcon className='w-6 h-6'/>
<span className='text-lg font-mono'>
{user.name}
</span>
</div>
<div className='py-4'>
{user.machines.map(machine => (
<div key={machine.id} className='flex items-center w-full gap-4'>
<StatusCircle isOnline={machine.online} className='w-4 h-4 px-1 w-fit'/>
<Attribute name={`Node ${machine.id}`} value={machine.givenName}/>
</div>
))}
</div>
</div>
))}
</div>
)
}