refactor resources

This commit is contained in:
Milo Schwartz
2024-11-11 00:00:16 -05:00
parent e77fb37ef1
commit 36bbb412dd
7 changed files with 441 additions and 378 deletions

View File

@@ -1,241 +0,0 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import {
CaretSortIcon,
CheckIcon,
} from "@radix-ui/react-icons";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { cn } from "@/lib/utils";
import { toast } from "@/hooks/useToast";
import { Button} from "@/components/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import React, { useState, useEffect } from "react";
import { api } from "@/api";
import { useParams } from "next/navigation";
import { useRouter } from "next/navigation";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ListSitesResponse } from "@server/routers/site";
import { AxiosResponse } from "axios";
import CustomDomainInput from "./CustomDomainInput";
const method = [
{ label: "Wireguard", value: "wg" },
{ label: "Newt", value: "newt" },
] as const;
const accountFormSchema = z.object({
subdomain: z
.string()
.min(2, {
message: "Name must be at least 2 characters.",
})
.max(30, {
message: "Name must not be longer than 30 characters.",
}),
name: z.string(),
siteId: z.number(),
});
type AccountFormValues = z.infer<typeof accountFormSchema>;
const defaultValues: Partial<AccountFormValues> = {
subdomain: "someanimalherefromapi",
name: "My Resource",
};
export function CreateResourceForm() {
const params = useParams();
const orgId = params.orgId;
const router = useRouter();
const [sites, setSites] = useState<ListSitesResponse["sites"]>([]);
const [domainSuffix, setDomainSuffix] = useState<string>(".example.com");
const form = useForm<AccountFormValues>({
resolver: zodResolver(accountFormSchema),
defaultValues,
});
useEffect(() => {
if (typeof window !== "undefined") {
const fetchSites = async () => {
const res = await api.get<AxiosResponse<ListSitesResponse>>(
`/org/${orgId}/sites/`
);
setSites(res.data.data.sites);
};
fetchSites();
}
}, []);
async function onSubmit(data: AccountFormValues) {
console.log(data);
const res = await api
.put(`/org/${orgId}/site/${data.siteId}/resource/`, {
name: data.name,
subdomain: data.subdomain,
// subdomain: data.subdomain,
})
.catch((e) => {
toast({
title: "Error creating resource...",
});
});
if (res && res.status === 201) {
const niceId = res.data.data.niceId;
// navigate to the resource page
router.push(`/${orgId}/settings/resources/${niceId}`);
}
}
return (
<>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-4"
>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="Your name" {...field} />
</FormControl>
<FormDescription>
This is the name that will be displayed for
this resource.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="subdomain"
render={({ field }) => (
<FormItem>
<FormLabel>Subdomain</FormLabel>
<FormControl>
{/* <Input placeholder="Your name" {...field} /> */}
<CustomDomainInput
{...field}
domainSuffix={domainSuffix}
placeholder="Enter subdomain"
/>
</FormControl>
<FormDescription>
This is the fully qualified domain name that
will be used to access the resource.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="siteId"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Site</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-[350px] justify-between",
!field.value &&
"text-muted-foreground"
)}
>
{field.value
? sites.find(
(site) =>
site.siteId ===
field.value
)?.name
: "Select site"}
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-[350px] p-0">
<Command>
<CommandInput placeholder="Search site..." />
<CommandList>
<CommandEmpty>
No site found.
</CommandEmpty>
<CommandGroup>
{sites.map((site) => (
<CommandItem
value={site.name}
key={site.siteId}
onSelect={() => {
form.setValue(
"siteId",
site.siteId
);
}}
>
<CheckIcon
className={cn(
"mr-2 h-4 w-4",
site.siteId ===
field.value
? "opacity-100"
: "opacity-0"
)}
/>
{site.name}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<FormDescription>
This is the site that will be used in the
dashboard.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Create Resource</Button>
</form>
</Form>
</>
);
}

View File

@@ -29,7 +29,7 @@ export default function CustomDomainInput(
};
return (
<div className="relative w-full max-w-sm">
<div className="w-full">
<div className="flex">
<Input
type="text"

View File

@@ -46,7 +46,7 @@ const GeneralFormSchema = z.object({
type GeneralFormValues = z.infer<typeof GeneralFormSchema>;
export function GeneralForm() {
export default function GeneralForm() {
const params = useParams();
const orgId = params.orgId;
const { resource, updateResource } = useResourceContext();

View File

@@ -5,6 +5,8 @@ import { AxiosResponse } from "axios";
import { redirect } from "next/navigation";
import { authCookieHeader } from "@app/api/cookies";
import { SidebarSettings } from "@app/components/SidebarSettings";
import Link from "next/link";
import { ArrowLeft } from "lucide-react";
interface ResourceLayoutProps {
children: React.ReactNode;
@@ -18,22 +20,20 @@ export default async function ResourceLayout(props: ResourceLayoutProps) {
let resource = null;
if (params.resourceId !== "create") {
try {
const res = await internal.get<AxiosResponse<GetResourceResponse>>(
`/resource/${params.resourceId}`,
await authCookieHeader()
);
resource = res.data.data;
} catch {
redirect(`/${params.orgId}/settings/resources`);
}
try {
const res = await internal.get<AxiosResponse<GetResourceResponse>>(
`/resource/${params.resourceId}`,
await authCookieHeader()
);
resource = res.data.data;
} catch {
redirect(`/${params.orgId}/settings/resources`);
}
const sidebarNavItems = [
{
title: "General",
href: `/{orgId}/settings/resources/resourceId`,
href: `/{orgId}/settings/resources/{resourceId}/general`,
},
{
title: "Targets",
@@ -41,27 +41,31 @@ export default async function ResourceLayout(props: ResourceLayoutProps) {
},
];
const isCreate = params.resourceId === "create";
return (
<>
<div className="mb-4">
<Link
href="../../"
className="text-muted-foreground hover:underline"
>
<div className="flex flex-row items-center gap-1">
<ArrowLeft /> <span>All Resources</span>
</div>
</Link>
</div>
<div className="space-y-0.5 select-none mb-6">
<h2 className="text-2xl font-bold tracking-tight">
{isCreate ? "New Resource" : resource?.name + " Settings"}
{resource?.name + " Settings"}
</h2>
<p className="text-muted-foreground">
{isCreate
? "Create a new resource"
: "Configure the settings on your resource: " +
resource?.name || ""}
.
Configure the settings on your resource
</p>
</div>
<ResourceProvider resource={resource}>
<SidebarSettings
sidebarNavItems={sidebarNavItems}
disabled={isCreate}
limitWidth={true}
>
{children}

View File

@@ -1,29 +1,10 @@
import React from "react";
import { Separator } from "@/components/ui/separator";
import { CreateResourceForm } from "./components/CreateResource";
import { GeneralForm } from "./components/GeneralForm";
import { redirect } from "next/navigation";
export default async function ResourcePage(props: {
params: Promise<{ resourceId: number | string }>;
params: Promise<{ resourceId: number | string; orgId: string }>;
}) {
const params = await props.params;
const isCreate = params.resourceId === "create";
return (
<div className="space-y-6">
<div>
<h3 className="text-lg font-medium">
{isCreate ? "Create Resource" : "General"}
</h3>
<p className="text-sm text-muted-foreground">
{isCreate
? "Create a new resource"
: "Edit basic resource settings"}
</p>
</div>
<Separator />
{isCreate ? <CreateResourceForm /> : <GeneralForm />}
</div>
redirect(
`/${params.orgId}/settings/resources/${params.resourceId}/general`
);
}