From 38a66ae2b5bc6e1539674b181ec0444d734994a1 Mon Sep 17 00:00:00 2001 From: brian Date: Sat, 18 Nov 2023 02:23:53 -0500 Subject: [PATCH] Started implementing editing of routes --- app/page.tsx | 4 +- components/EditRoute.tsx | 0 components/ListItem.tsx | 2 +- components/RouteCards.tsx | 125 ++++++++++++++++++++++----- components/ui/dialog.tsx | 122 ++++++++++++++++++++++++++ components/ui/form.tsx | 176 ++++++++++++++++++++++++++++++++++++++ components/ui/input.tsx | 25 ++++++ components/ui/label.tsx | 26 ++++++ lib/clientActions.ts | 19 +--- package.json | 7 +- 10 files changed, 464 insertions(+), 42 deletions(-) create mode 100644 components/EditRoute.tsx create mode 100644 components/ui/dialog.tsx create mode 100644 components/ui/form.tsx create mode 100644 components/ui/input.tsx create mode 100644 components/ui/label.tsx diff --git a/app/page.tsx b/app/page.tsx index fca34ca..08076be 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -36,8 +36,8 @@ export default function Home() {
- +
) -} +} \ No newline at end of file diff --git a/components/EditRoute.tsx b/components/EditRoute.tsx new file mode 100644 index 0000000..e69de29 diff --git a/components/ListItem.tsx b/components/ListItem.tsx index 07d4ba6..14dffab 100644 --- a/components/ListItem.tsx +++ b/components/ListItem.tsx @@ -1,6 +1,6 @@ import { cn } from "@/lib/utils" -import { NavigationMenuLink } from "@radix-ui/react-navigation-menu" import React from "react" +import { NavigationMenu, NavigationMenuLink } from "./ui/navigation-menu" const ListItem = React.forwardRef< React.ElementRef<"a">, diff --git a/components/RouteCards.tsx b/components/RouteCards.tsx index 1c3ecc7..d9f083a 100644 --- a/components/RouteCards.tsx +++ b/components/RouteCards.tsx @@ -4,13 +4,20 @@ import { Button } from '@/components/ui/button'; import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "./ui/card"; import { getHosts, getRouteUpstreams } from '@/lib/utils'; import { toast } from './ui/use-toast'; -import React from 'react'; +import React, { useState } from 'react'; import useSWRMutation from 'swr/mutation'; import { deleteRoute } from '@/lib/clientActions'; +import { Dialog, DialogContent, DialogHeader } from './ui/dialog'; +import { DialogDescription, DialogTitle } from '@radix-ui/react-dialog'; +import { Form, FormControl, FormField, FormItem, FormLabel } from './ui/form'; +import { useForm } from 'react-hook-form'; +import { Input } from './ui/input'; export default function RouteCards({ routes }: { routes: Route[] }) { - const { trigger } = useSWRMutation("/api/caddy/routes", deleteRoute, { + const [route, setRoute] = useState(); + + const { trigger: handleDelete } = useSWRMutation("/api/caddy/routes", deleteRoute, { onError: () => { return toast({ title: "Error", @@ -27,30 +34,102 @@ export default function RouteCards({ routes }: { routes: Route[] }) { } }); + + let editRoute = (route: Route) => { + setRoute(route); + } + return ( - routes.map((route, index) => ( -
- - - {getHosts(route).values().next().value} - {getHosts(route).map((route => {route}))} - - - -

- Tunneling to -

-

{getRouteUpstreams(route)?.map((upstream => upstream.dial))}

-
- - - - -
-
- )) + <> + + { + routes.map((route, index) => ( +
+ + + {getHosts(route).values().next().value} + {getHosts(route).map((route => {route}))} + + + +

+ Tunneling to +

+

{getRouteUpstreams(route)?.map((upstream => upstream.dial))}

+
+ + + + +
+
+ ))} + ) } +export function EditRouteDialog({ route }: { route?: Route }) { + const form = useForm(); + let onSubmit = ((data: any) => { + console.log(JSON.stringify(data)) + }) + let targetRoute: Route = route as Route; + return ( + + +
+ + ( + + Handler + + + + + )} + > + {route ? getRouteUpstreams(targetRoute)?.map((upstream, index) => ( + ( + + Upstream {index + 1} + + + + + )} + > + + )) : <>} + + {route ? getHosts(targetRoute)?.map((host, index) => ( + ( + + Upstream {index + 1} + + + + + )} + > + + )) : <>} + +
+ +
+
+ ) +} \ No newline at end of file diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx new file mode 100644 index 0000000..ab1b621 --- /dev/null +++ b/components/ui/dialog.tsx @@ -0,0 +1,122 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { Cross2Icon } from "@radix-ui/react-icons" + +import { cn } from "@/lib/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogTrigger, + DialogClose, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/components/ui/form.tsx b/components/ui/form.tsx new file mode 100644 index 0000000..f6afdaf --- /dev/null +++ b/components/ui/form.tsx @@ -0,0 +1,176 @@ +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { Slot } from "@radix-ui/react-slot" +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from "react-hook-form" + +import { cn } from "@/lib/utils" +import { Label } from "@/components/ui/label" + +const Form = FormProvider + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath +> = { + name: TName +} + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue +) + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath +>({ + ...props +}: ControllerProps) => { + return ( + + + + ) +} + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext) + const itemContext = React.useContext(FormItemContext) + const { getFieldState, formState } = useFormContext() + + const fieldState = getFieldState(fieldContext.name, formState) + + if (!fieldContext) { + throw new Error("useFormField should be used within ") + } + + const { id } = itemContext + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + } +} + +type FormItemContextValue = { + id: string +} + +const FormItemContext = React.createContext( + {} as FormItemContextValue +) + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId() + + return ( + +
+ + ) +}) +FormItem.displayName = "FormItem" + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField() + + return ( +