feat: refactor all dashboard routes

This commit is contained in:
Aarnav Tale 2024-12-08 13:52:02 -05:00
parent 50e43bc0c3
commit 6cf343d623
No known key found for this signature in database
39 changed files with 48 additions and 66 deletions

View file

@ -0,0 +1,122 @@
import React, { useEffect } from 'react'
import Merge from 'react-codemirror-merge'
import CodeMirror from '@uiw/react-codemirror'
import * as shopify from '@shopify/lang-jsonc'
import { ClientOnly } from 'remix-utils/client-only'
import { ErrorBoundary } from 'react-error-boundary'
import { githubDark, githubLight } from '@uiw/codemirror-theme-github'
import { useState } from 'react'
import { cn } from '~/utils/cn'
import Fallback from './fallback'
interface EditorProps {
isDisabled?: boolean
value: string
onChange: (value: string) => void
}
export function Editor(props: EditorProps) {
const [light, setLight] = useState(false)
useEffect(() => {
const theme = window.matchMedia('(prefers-color-scheme: light)')
setLight(theme.matches)
theme.addEventListener('change', (theme) => {
setLight(theme.matches)
})
})
return (
<div className={cn(
'border border-gray-200 dark:border-gray-700',
'rounded-b-lg rounded-tr-lg mb-2 z-10 overflow-x-hidden',
)}>
<div className="overflow-y-scroll h-editor text-sm">
<ErrorBoundary fallback={
<p className={cn(
'w-full h-full flex items-center justify-center',
'text-gray-400 dark:text-gray-500 text-xl',
)}>
Failed to load the editor.
</p>
}>
<ClientOnly fallback={<Fallback acl={props.value} />}>
{() => (
<CodeMirror
value={props.value}
height="100%"
extensions={[shopify.jsonc()]}
style={{ height: "100%" }}
theme={light ? githubLight : githubDark}
onChange={(value) => props.onChange(value)}
/>
)}
</ClientOnly>
</ErrorBoundary>
</div>
</div>
)
}
interface DifferProps {
left: string
right: string
}
export function Differ(props: DifferProps) {
const [light, setLight] = useState(false)
useEffect(() => {
const theme = window.matchMedia('(prefers-color-scheme: light)')
setLight(theme.matches)
theme.addEventListener('change', (theme) => {
setLight(theme.matches)
})
})
return (
<div className={cn(
'border border-gray-200 dark:border-gray-700',
'rounded-b-lg rounded-tr-lg mb-2 z-10 overflow-x-hidden',
)}>
<div className="overflow-y-scroll h-editor text-sm">
{props.left === props.right ? (
<p className={cn(
'w-full h-full flex items-center justify-center',
'text-gray-400 dark:text-gray-500 text-xl',
)}>
No changes
</p>
) : (
<ErrorBoundary fallback={
<p className={cn(
'w-full h-full flex items-center justify-center',
'text-gray-400 dark:text-gray-500 text-xl',
)}>
Failed to load the editor.
</p>
}>
<ClientOnly fallback={<Fallback acl={props.right} />}>
{() => (
<Merge
orientation="a-b"
theme={light ? githubLight : githubDark}
>
<Merge.Original
readOnly
value={props.left}
extensions={[shopify.jsonc()]}
/>
<Merge.Modified
readOnly
value={props.right}
extensions={[shopify.jsonc()]}
/>
</Merge>
)}
</ClientOnly>
</ErrorBoundary>
)}
</div>
</div>
)
}

View file

@ -0,0 +1,30 @@
import { cn } from '~/utils/cn'
import { AlertIcon } from '@primer/octicons-react'
import Card from '~/components/Card'
import Code from '~/components/Code'
interface Props {
message: string
}
export function ErrorView({ message }: Props) {
return (
<Card variant="flat" className="max-w-full mb-4">
<div className="flex items-center justify-between">
<Card.Title className="text-xl mb-0">
Error
</Card.Title>
<AlertIcon className="w-8 h-8 text-red-500"/>
</div>
<Card.Text className="mt-4">
Could not apply changes to your ACL policy
due to the following error:
<br />
<Code>
{message}
</Code>
</Card.Text>
</Card>
)
}

View file

@ -0,0 +1,24 @@
import Spinner from '~/components/Spinner'
import { cn } from '~/utils/cn'
interface Props {
readonly acl: string
}
export default function Fallback({ acl }: Props) {
return (
<div className="inline-block relative w-full h-editor">
<Spinner className="w-4 h-4 absolute p-2" />
<textarea
readOnly
className={cn(
'w-full h-editor font-mono resize-none',
'text-sm text-gray-600 dark:text-gray-300',
'bg-ui-100 dark:bg-ui-800',
'pl-10 pt-1 leading-snug',
)}
value={acl}
/>
</div>
)
}

View file

@ -0,0 +1,43 @@
import { cn } from '~/utils/cn'
import { AlertIcon } from '@primer/octicons-react'
import Code from '~/components/Code'
import Card from '~/components/Card'
interface Props {
mode: 'file' | 'database'
}
export function Unavailable({ mode }: Props) {
return (
<Card variant="flat" className="max-w-prose mt-12">
<div className="flex items-center justify-between">
<Card.Title className="text-xl mb-0">
ACL Policy Unavailable
</Card.Title>
<AlertIcon className="w-8 h-8 text-red-500"/>
</div>
<Card.Text className="mt-4">
Unable to load a valid ACL policy configuration.
This is most likely due to a misconfiguration in your
Headscale configuration file.
</Card.Text>
{mode !== 'file' ? (
<p className="mt-4 text-sm">
According to your configuration, the ACL policy mode
is set to <Code>file</Code> but the ACL file is not
available. Ensure that the <Code>policy.path</Code> is
set to a valid path in your Headscale configuration.
</p>
) : (
<p className="mt-4 text-sm">
In order to fully utilize the ACL management features of
Headplane, please set <Code>policy.mode</Code> to either
{' '}<Code>file</Code> or <Code>database</Code> in your
Headscale configuration.
</p>
)}
</Card>
)
}