fix: resolve type errors and lint components/routes
This commit is contained in:
parent
414b95c293
commit
6745ee8529
@ -1,4 +1,4 @@
|
|||||||
import { useState, type HTMLProps } from 'react';
|
import { useState, HTMLProps } from 'react';
|
||||||
import { CopyIcon, CheckIcon } from '@primer/octicons-react';
|
import { CopyIcon, CheckIcon } from '@primer/octicons-react';
|
||||||
import { cn } from '~/utils/cn';
|
import { cn } from '~/utils/cn';
|
||||||
import { toast } from '~/components/Toaster';
|
import { toast } from '~/components/Toaster';
|
||||||
@ -20,7 +20,7 @@ export default function Code(props: Props) {
|
|||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</code>
|
</code>
|
||||||
{props.isCopyable && props.children ? (
|
{props.isCopyable ? (
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
'ml-1 p-1 rounded-md',
|
'ml-1 p-1 rounded-md',
|
||||||
@ -29,6 +29,10 @@ export default function Code(props: Props) {
|
|||||||
'inline-flex items-center justify-center',
|
'inline-flex items-center justify-center',
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (!props.children) {
|
||||||
|
throw new Error('Made copyable without children');
|
||||||
|
}
|
||||||
|
|
||||||
navigator.clipboard.writeText(props.children.join(''));
|
navigator.clipboard.writeText(props.children.join(''));
|
||||||
toast('Copied to clipboard');
|
toast('Copied to clipboard');
|
||||||
setIsCopied(true);
|
setIsCopied(true);
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
import { cn } from '~/utils/cn';
|
import { cn } from '~/utils/cn';
|
||||||
import Link from '~/components/Link';
|
import Link from '~/components/Link';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
const __VERSION__: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface FooterProps {
|
interface FooterProps {
|
||||||
url: string;
|
url: string;
|
||||||
debug: boolean;
|
debug: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Footer({ url, debug, integration }: FooterProps) {
|
export default function Footer({ url, debug }: FooterProps) {
|
||||||
return (
|
return (
|
||||||
<footer
|
<footer
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@ -1,7 +1,15 @@
|
|||||||
import { XIcon } from '@primer/octicons-react';
|
import { XIcon } from '@primer/octicons-react';
|
||||||
import { type AriaToastProps, useToast, useToastRegion } from '@react-aria/toast';
|
import {
|
||||||
import { ToastQueue, type ToastState, useToastQueue } from '@react-stately/toast';
|
AriaToastProps,
|
||||||
import { type ReactNode, useRef } from 'react';
|
useToast,
|
||||||
|
useToastRegion,
|
||||||
|
} from '@react-aria/toast';
|
||||||
|
import {
|
||||||
|
ToastQueue,
|
||||||
|
ToastState,
|
||||||
|
useToastQueue,
|
||||||
|
} from '@react-stately/toast';
|
||||||
|
import { ReactNode, useRef } from 'react';
|
||||||
import { Button } from 'react-aria-components';
|
import { Button } from 'react-aria-components';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { ClientOnly } from 'remix-utils/client-only';
|
import { ClientOnly } from 'remix-utils/client-only';
|
||||||
@ -14,7 +22,6 @@ type ToastProps = AriaToastProps<ReactNode> & {
|
|||||||
function Toast({ state, ...properties }: ToastProps) {
|
function Toast({ state, ...properties }: ToastProps) {
|
||||||
const reference = useRef(null);
|
const reference = useRef(null);
|
||||||
|
|
||||||
// @ts-expect-error: RefObject doesn't map to FocusableElement?
|
|
||||||
const { toastProps, titleProps, closeButtonProps } = useToast(
|
const { toastProps, titleProps, closeButtonProps } = useToast(
|
||||||
properties,
|
properties,
|
||||||
state,
|
state,
|
||||||
@ -58,13 +65,11 @@ export function Toaster() {
|
|||||||
const reference = useRef(null);
|
const reference = useRef(null);
|
||||||
const state = useToastQueue(toasts);
|
const state = useToastQueue(toasts);
|
||||||
|
|
||||||
// @ts-expect-error: React 19 has weird types for Portal vs Node
|
|
||||||
const { regionProps } = useToastRegion({}, state, reference);
|
const { regionProps } = useToastRegion({}, state, reference);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
{
|
{
|
||||||
// @ts-expect-error: Portal doesn't match Node in React 19 yet
|
|
||||||
() =>
|
() =>
|
||||||
createPortal(
|
createPortal(
|
||||||
state.visibleToasts.length >= 0 ? (
|
state.visibleToasts.length >= 0 ? (
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import {
|
|||||||
PencilIcon,
|
PencilIcon,
|
||||||
} from '@primer/octicons-react';
|
} from '@primer/octicons-react';
|
||||||
import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
|
import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
|
||||||
import { useLoaderData, useRevalidator } from 'react-router';
|
import { useLoaderData, useRevalidator, useFetcher } from 'react-router';
|
||||||
import { useDebounceFetcher } from 'remix-utils/use-debounce-fetcher';
|
//import { useDebounceFetcher } from 'remix-utils/use-debounce-fetcher';
|
||||||
import { useEffect, useState, useMemo } from 'react';
|
import { useEffect, useState, useMemo } from 'react';
|
||||||
import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
|
import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
|
||||||
import { setTimeout } from 'node:timers/promises';
|
import { setTimeout } from 'node:timers/promises';
|
||||||
@ -149,7 +149,7 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const data = useLoaderData<typeof loader>();
|
const data = useLoaderData<typeof loader>();
|
||||||
const fetcher = useDebounceFetcher<typeof action>();
|
const fetcher = useFetcher<typeof action>();
|
||||||
const revalidator = useRevalidator();
|
const revalidator = useRevalidator();
|
||||||
|
|
||||||
const [acl, setAcl] = useState(data.policy ?? '');
|
const [acl, setAcl] = useState(data.policy ?? '');
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
import { type ActionFunctionArgs, type LoaderFunctionArgs, redirect } from 'react-router';
|
import {
|
||||||
|
type ActionFunctionArgs,
|
||||||
|
type LoaderFunctionArgs,
|
||||||
|
redirect,
|
||||||
|
} from 'react-router';
|
||||||
import { Form, useActionData, useLoaderData } from 'react-router';
|
import { Form, useActionData, useLoaderData } from 'react-router';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export default function Nameservers({ nameservers, isDisabled }: Props) {
|
|||||||
interface ListProps {
|
interface ListProps {
|
||||||
isGlobal: boolean;
|
isGlobal: boolean;
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
nameservers: string[];
|
nameservers: Record<string, string[]>;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -43,22 +43,22 @@ export async function loader() {
|
|||||||
export async function action({ request }: ActionFunctionArgs) {
|
export async function action({ request }: ActionFunctionArgs) {
|
||||||
const session = await getSession(request.headers.get('Cookie'));
|
const session = await getSession(request.headers.get('Cookie'));
|
||||||
if (!session.has('hsApiKey')) {
|
if (!session.has('hsApiKey')) {
|
||||||
return send({ success: false }, 401);
|
return data({ success: false }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = await loadContext();
|
const context = await loadContext();
|
||||||
if (!context.config.write) {
|
if (!context.config.write) {
|
||||||
return send({ success: false }, 403);
|
return data({ success: false }, { status: 403 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = (await request.json()) as Record<string, unknown>;
|
const patch = (await request.json()) as Record<string, unknown>;
|
||||||
await patchConfig(data);
|
await patchConfig(patch);
|
||||||
|
|
||||||
if (context.integration?.onConfigChange) {
|
if (context.integration?.onConfigChange) {
|
||||||
await context.integration.onConfigChange(context.integration.context);
|
await context.integration.onConfigChange(context.integration.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: true };
|
return data({ success: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { ChevronDownIcon, CopyIcon } from '@primer/octicons-react';
|
import { ChevronDownIcon, CopyIcon } from '@primer/octicons-react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import { useMemo } from 'react';
|
||||||
import Menu from '~/components/Menu';
|
import Menu from '~/components/Menu';
|
||||||
import StatusCircle from '~/components/StatusCircle';
|
import StatusCircle from '~/components/StatusCircle';
|
||||||
import { toast } from '~/components/Toaster';
|
import { toast } from '~/components/Toaster';
|
||||||
@ -35,7 +35,11 @@ export default function MachineRow({ machine, routes, magic, users }: Props) {
|
|||||||
: magic;
|
: magic;
|
||||||
|
|
||||||
// This is much easier with Object.groupBy but it's too new for us
|
// This is much easier with Object.groupBy but it's too new for us
|
||||||
const { exit, subnet, subnetApproved } = routes.reduce(
|
const { exit, subnet, subnetApproved } = routes.reduce<{
|
||||||
|
exit: Route[];
|
||||||
|
subnetApproved: Route[];
|
||||||
|
subnet: Route[];
|
||||||
|
}>(
|
||||||
(acc, route) => {
|
(acc, route) => {
|
||||||
if (route.prefix === '::/0' || route.prefix === '0.0.0.0/0') {
|
if (route.prefix === '::/0' || route.prefix === '0.0.0.0/0') {
|
||||||
acc.exit.push(route);
|
acc.exit.push(route);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useFetcher } from 'react-router';
|
import { useFetcher } from 'react-router';
|
||||||
import { type Dispatch, type SetStateAction, useMemo } from 'react';
|
import { Dispatch, SetStateAction, useMemo } from 'react';
|
||||||
|
|
||||||
import Dialog from '~/components/Dialog';
|
import Dialog from '~/components/Dialog';
|
||||||
import Switch from '~/components/Switch';
|
import Switch from '~/components/Switch';
|
||||||
@ -18,7 +18,10 @@ export default function Routes({ machine, routes, state }: RoutesProps) {
|
|||||||
const fetcher = useFetcher();
|
const fetcher = useFetcher();
|
||||||
|
|
||||||
// This is much easier with Object.groupBy but it's too new for us
|
// This is much easier with Object.groupBy but it's too new for us
|
||||||
const { exit, subnet } = routes.reduce(
|
const { exit, subnet } = routes.reduce<{
|
||||||
|
exit: Route[];
|
||||||
|
subnet: Route[];
|
||||||
|
}>(
|
||||||
(acc, route) => {
|
(acc, route) => {
|
||||||
if (route.prefix === '::/0' || route.prefix === '0.0.0.0/0') {
|
if (route.prefix === '::/0' || route.prefix === '0.0.0.0/0') {
|
||||||
acc.exit.push(route);
|
acc.exit.push(route);
|
||||||
|
|||||||
@ -81,7 +81,11 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is much easier with Object.groupBy but it's too new for us
|
// This is much easier with Object.groupBy but it's too new for us
|
||||||
const { exit, subnet, subnetApproved } = routes.reduce(
|
const { exit, subnet, subnetApproved } = routes.reduce<{
|
||||||
|
exit: Route[];
|
||||||
|
subnet: Route[];
|
||||||
|
subnetApproved: Route[];
|
||||||
|
}>(
|
||||||
(acc, route) => {
|
(acc, route) => {
|
||||||
if (route.prefix === '::/0' || route.prefix === '0.0.0.0/0') {
|
if (route.prefix === '::/0' || route.prefix === '0.0.0.0/0') {
|
||||||
acc.exit.push(route);
|
acc.exit.push(route);
|
||||||
@ -133,7 +137,6 @@ export default function Page() {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<MenuOptions
|
<MenuOptions
|
||||||
className={cn('bg-ui-100 dark:bg-ui-800')}
|
|
||||||
machine={machine}
|
machine={machine}
|
||||||
routes={routes}
|
routes={routes}
|
||||||
users={users}
|
users={users}
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
import { type DataRef, DndContext, useDraggable, useDroppable } from '@dnd-kit/core';
|
import {
|
||||||
|
DataRef,
|
||||||
|
DndContext,
|
||||||
|
useDraggable,
|
||||||
|
useDroppable,
|
||||||
|
} from '@dnd-kit/core';
|
||||||
import { PersonIcon } from '@primer/octicons-react';
|
import { PersonIcon } from '@primer/octicons-react';
|
||||||
import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
|
import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
|
||||||
import { useActionData, useLoaderData, useSubmit } from 'react-router';
|
import { useActionData, useLoaderData, useSubmit } from 'react-router';
|
||||||
|
|||||||
@ -22,5 +22,5 @@ export async function loader() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { status: 'OK' };
|
return data({ status: 'OK' });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,10 @@
|
|||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": true
|
"recommended": true,
|
||||||
|
"style": {
|
||||||
|
"useImportType": "off"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"javascript": {
|
"javascript": {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user