mirror of
https://github.com/fosrl/pangolin.git
synced 2025-06-24 14:18:55 +02:00
refactor sites settings general form
This commit is contained in:
parent
a7955cb8d2
commit
e77fb37ef1
25 changed files with 159 additions and 367 deletions
|
@ -76,7 +76,7 @@ export async function listSites(
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedParams.error.errors.map((e) => e.message).join(", ")
|
fromError(parsedParams.error)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@ export default function AccessControlsPage() {
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-8"
|
className="space-y-4"
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|
|
@ -5,5 +5,4 @@ export default async function UserPage(props: {
|
||||||
}) {
|
}) {
|
||||||
const { orgId, userId } = await props.params;
|
const { orgId, userId } = await props.params;
|
||||||
redirect(`/${orgId}/settings/access/users/${userId}/access-controls`);
|
redirect(`/${orgId}/settings/access/users/${userId}/access-controls`);
|
||||||
return <></>;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@ export function CreateResourceForm() {
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-8"
|
className="space-y-4"
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|
|
@ -97,7 +97,7 @@ export function GeneralForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|
|
@ -1,218 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { ChevronDownIcon } 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, buttonVariants } from "@/components/ui/button";
|
|
||||||
import {
|
|
||||||
Form,
|
|
||||||
FormControl,
|
|
||||||
FormDescription,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { generateKeypair } from "./wireguardConfig";
|
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { api } from "@/api";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { Checkbox } from "@app/components/ui/checkbox";
|
|
||||||
import { PickSiteDefaultsResponse } from "@server/routers/site";
|
|
||||||
import CopyTextBox from "@app/components/CopyTextBox";
|
|
||||||
|
|
||||||
const method = [
|
|
||||||
{ label: "Wireguard", value: "wg" },
|
|
||||||
{ label: "Newt", value: "newt" },
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
const accountFormSchema = z.object({
|
|
||||||
name: z
|
|
||||||
.string()
|
|
||||||
.min(2, {
|
|
||||||
message: "Name must be at least 2 characters.",
|
|
||||||
})
|
|
||||||
.max(30, {
|
|
||||||
message: "Name must not be longer than 30 characters.",
|
|
||||||
}),
|
|
||||||
method: z.enum(["wg", "newt"]),
|
|
||||||
});
|
|
||||||
|
|
||||||
type AccountFormValues = z.infer<typeof accountFormSchema>;
|
|
||||||
|
|
||||||
const defaultValues: Partial<AccountFormValues> = {
|
|
||||||
name: "",
|
|
||||||
method: "wg",
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CreateSiteForm() {
|
|
||||||
const params = useParams();
|
|
||||||
const orgId = params.orgId;
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const [keypair, setKeypair] = useState<{
|
|
||||||
publicKey: string;
|
|
||||||
privateKey: string;
|
|
||||||
} | null>(null);
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const [isChecked, setIsChecked] = useState(false);
|
|
||||||
const [siteDefaults, setSiteDefaults] =
|
|
||||||
useState<PickSiteDefaultsResponse | null>(null);
|
|
||||||
|
|
||||||
const handleCheckboxChange = (checked: boolean) => {
|
|
||||||
setIsChecked(checked);
|
|
||||||
};
|
|
||||||
|
|
||||||
const form = useForm<AccountFormValues>({
|
|
||||||
resolver: zodResolver(accountFormSchema),
|
|
||||||
defaultValues,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
const generatedKeypair = generateKeypair();
|
|
||||||
setKeypair(generatedKeypair);
|
|
||||||
setIsLoading(false);
|
|
||||||
|
|
||||||
api.get(`/org/${orgId}/pick-site-defaults`)
|
|
||||||
.catch((e) => {
|
|
||||||
toast({
|
|
||||||
title: "Error creating site...",
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res && res.status === 200) {
|
|
||||||
setSiteDefaults(res.data.data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
async function onSubmit(data: AccountFormValues) {
|
|
||||||
const res = await api
|
|
||||||
.put(`/org/${orgId}/site/`, {
|
|
||||||
name: data.name,
|
|
||||||
subnet: siteDefaults?.subnet,
|
|
||||||
exitNodeId: siteDefaults?.exitNodeId,
|
|
||||||
pubKey: keypair?.publicKey,
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
toast({
|
|
||||||
title: "Error creating site...",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res && res.status === 201) {
|
|
||||||
const niceId = res.data.data.niceId;
|
|
||||||
// navigate to the site page
|
|
||||||
router.push(`/${orgId}/settings/sites/${niceId}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const wgConfig =
|
|
||||||
keypair && siteDefaults
|
|
||||||
? `[Interface]
|
|
||||||
Address = ${siteDefaults.subnet}
|
|
||||||
ListenPort = 51820
|
|
||||||
PrivateKey = ${keypair.privateKey}
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = ${siteDefaults.publicKey}
|
|
||||||
AllowedIPs = ${siteDefaults.address.split("/")[0]}/32
|
|
||||||
Endpoint = ${siteDefaults.endpoint}:${siteDefaults.listenPort}
|
|
||||||
PersistentKeepalive = 5`
|
|
||||||
: "";
|
|
||||||
|
|
||||||
const newtConfig = `curl -fsSL https://get.docker.com -o get-docker.sh
|
|
||||||
sh get-docker.sh`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form {...form}>
|
|
||||||
<form
|
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
|
||||||
className="space-y-8"
|
|
||||||
>
|
|
||||||
<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 site.
|
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="method"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Method</FormLabel>
|
|
||||||
<div className="relative w-max">
|
|
||||||
<FormControl>
|
|
||||||
<select
|
|
||||||
className={cn(
|
|
||||||
buttonVariants({
|
|
||||||
variant: "outline",
|
|
||||||
}),
|
|
||||||
"w-[200px] appearance-none font-normal"
|
|
||||||
)}
|
|
||||||
{...field}
|
|
||||||
>
|
|
||||||
<option value="wg">
|
|
||||||
WireGuard
|
|
||||||
</option>
|
|
||||||
<option value="newt">Newt</option>
|
|
||||||
</select>
|
|
||||||
</FormControl>
|
|
||||||
<ChevronDownIcon className="absolute right-3 top-2.5 h-4 w-4 opacity-50" />
|
|
||||||
</div>
|
|
||||||
<FormDescription>
|
|
||||||
This is how you will connect your site to
|
|
||||||
Fossorial.
|
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{form.watch("method") === "wg" && !isLoading ? (
|
|
||||||
<CopyTextBox text={wgConfig} />
|
|
||||||
) : form.watch("method") === "wg" && isLoading ? (
|
|
||||||
<p>Loading WireGuard configuration...</p>
|
|
||||||
) : (
|
|
||||||
<CopyTextBox text={newtConfig} wrapText={false} />
|
|
||||||
)}
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id="terms"
|
|
||||||
checked={isChecked}
|
|
||||||
onCheckedChange={handleCheckboxChange}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="terms"
|
|
||||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
||||||
>
|
|
||||||
I have copied the config
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<Button type="submit" disabled={!isChecked}>
|
|
||||||
Create Site
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { z } from "zod";
|
|
||||||
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 { useSiteContext } from "@app/hooks/useSiteContext";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import api from "@app/api";
|
|
||||||
import { useToast } from "@app/hooks/useToast";
|
|
||||||
|
|
||||||
const GeneralFormSchema = z.object({
|
|
||||||
name: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
type GeneralFormValues = z.infer<typeof GeneralFormSchema>;
|
|
||||||
|
|
||||||
export function GeneralForm() {
|
|
||||||
const { site, updateSite } = useSiteContext();
|
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const form = useForm<GeneralFormValues>({
|
|
||||||
resolver: zodResolver(GeneralFormSchema),
|
|
||||||
defaultValues: {
|
|
||||||
name: site?.name,
|
|
||||||
},
|
|
||||||
mode: "onChange",
|
|
||||||
});
|
|
||||||
|
|
||||||
async function onSubmit(data: GeneralFormValues) {
|
|
||||||
updateSite({ name: data.name });
|
|
||||||
|
|
||||||
await api
|
|
||||||
.post(`/site/${site?.siteId}`, {
|
|
||||||
name: data.name,
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: "Failed to update site",
|
|
||||||
description:
|
|
||||||
e.message ||
|
|
||||||
"An error occurred while updating the site.",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form {...form}>
|
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="name"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Name</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormDescription>
|
|
||||||
This is the display name of the site.
|
|
||||||
</FormDescription>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Button type="submit">Update Site</Button>
|
|
||||||
</form>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
}
|
|
99
src/app/[orgId]/settings/sites/[niceId]/general/page.tsx
Normal file
99
src/app/[orgId]/settings/sites/[niceId]/general/page.tsx
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
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 { useSiteContext } from "@app/hooks/useSiteContext";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import api from "@app/api";
|
||||||
|
import { useToast } from "@app/hooks/useToast";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
const GeneralFormSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
type GeneralFormValues = z.infer<typeof GeneralFormSchema>;
|
||||||
|
|
||||||
|
export default function GeneralPage() {
|
||||||
|
const { site, updateSite } = useSiteContext();
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const form = useForm<GeneralFormValues>({
|
||||||
|
resolver: zodResolver(GeneralFormSchema),
|
||||||
|
defaultValues: {
|
||||||
|
name: site?.name,
|
||||||
|
},
|
||||||
|
mode: "onChange",
|
||||||
|
});
|
||||||
|
|
||||||
|
async function onSubmit(data: GeneralFormValues) {
|
||||||
|
await api
|
||||||
|
.post(`/site/${site?.siteId}`, {
|
||||||
|
name: data.name,
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Failed to update site",
|
||||||
|
description:
|
||||||
|
e.message ||
|
||||||
|
"An error occurred while updating the site.",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSite({ name: data.name });
|
||||||
|
|
||||||
|
router.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="space-y-0.5 select-none mb-6">
|
||||||
|
<h2 className="text-2xl font-bold tracking-tight">
|
||||||
|
General Settings
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Configure the general settings for this site
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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 {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
This is the display name of the site
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button type="submit">Update Site</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ import { AxiosResponse } from "axios";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { authCookieHeader } from "@app/api/cookies";
|
import { authCookieHeader } from "@app/api/cookies";
|
||||||
import { SidebarSettings } from "@app/components/SidebarSettings";
|
import { SidebarSettings } from "@app/components/SidebarSettings";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { ArrowLeft } from "lucide-react";
|
||||||
|
|
||||||
interface SettingsLayoutProps {
|
interface SettingsLayoutProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
@ -17,50 +19,53 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
|
||||||
const { children } = props;
|
const { children } = props;
|
||||||
|
|
||||||
let site = null;
|
let site = null;
|
||||||
|
try {
|
||||||
if (params.niceId !== "create") {
|
const res = await internal.get<AxiosResponse<GetSiteResponse>>(
|
||||||
try {
|
`/org/${params.orgId}/site/${params.niceId}`,
|
||||||
const res = await internal.get<AxiosResponse<GetSiteResponse>>(
|
await authCookieHeader()
|
||||||
`/org/${params.orgId}/site/${params.niceId}`,
|
);
|
||||||
await authCookieHeader()
|
site = res.data.data;
|
||||||
);
|
} catch {
|
||||||
site = res.data.data;
|
redirect(`/${params.orgId}/settings/sites`);
|
||||||
} catch {
|
|
||||||
redirect(`/${params.orgId}/settings/sites`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sidebarNavItems = [
|
const sidebarNavItems = [
|
||||||
{
|
{
|
||||||
title: "General",
|
title: "General",
|
||||||
href: "/{orgId}/settings/sites/{niceId}",
|
href: "/{orgId}/settings/sites/{niceId}/general",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const isCreate = params.niceId === "create";
|
|
||||||
|
|
||||||
return (
|
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 Sites</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-0.5 select-none mb-6">
|
<div className="space-y-0.5 select-none mb-6">
|
||||||
<h2 className="text-2xl font-bold tracking-tight">
|
<h2 className="text-2xl font-bold tracking-tight">
|
||||||
{isCreate ? "New Site" : site?.name + " Settings"}
|
{site?.name + " Settings"}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
{isCreate
|
Configure the settings on your site
|
||||||
? "Create a new site"
|
|
||||||
: "Configure the settings on your site: " +
|
|
||||||
site?.name || ""}
|
|
||||||
.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SidebarSettings
|
<SiteProvider site={site}>
|
||||||
sidebarNavItems={sidebarNavItems}
|
<SidebarSettings
|
||||||
disabled={isCreate}
|
sidebarNavItems={sidebarNavItems}
|
||||||
limitWidth={true}
|
limitWidth={true}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</SidebarSettings>
|
</SidebarSettings>
|
||||||
|
</SiteProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,8 @@
|
||||||
import React from "react";
|
import { redirect } from "next/navigation";
|
||||||
import { Separator } from "@/components/ui/separator";
|
|
||||||
import { CreateSiteForm } from "./components/CreateSite";
|
|
||||||
import { GeneralForm } from "./components/GeneralForm";
|
|
||||||
|
|
||||||
export default async function SitePage(props: {
|
export default async function SitePage(props: {
|
||||||
params: Promise<{ niceId: string }>;
|
params: Promise<{ orgId: string; niceId: string }>;
|
||||||
}) {
|
}) {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
const isCreate = params.niceId === "create";
|
redirect(`/${params.orgId}/settings/sites/${params.niceId}/general`);
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-medium">
|
|
||||||
{isCreate ? "Create Site" : "General"}
|
|
||||||
</h3>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{isCreate
|
|
||||||
? "Create a new site"
|
|
||||||
: "Edit basic site settings"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Separator />
|
|
||||||
|
|
||||||
{isCreate ? <CreateSiteForm /> : <GeneralForm />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,6 +133,9 @@ export default function CreateSiteForm({ open, setOpen }: CreateSiteFormProps) {
|
||||||
const niceId = res.data.data.niceId;
|
const niceId = res.data.data.niceId;
|
||||||
// navigate to the site page
|
// navigate to the site page
|
||||||
router.push(`/${orgId}/settings/sites/${niceId}`);
|
router.push(`/${orgId}/settings/sites/${niceId}`);
|
||||||
|
|
||||||
|
// close the modal
|
||||||
|
setOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
@ -258,6 +261,11 @@ sh get-docker.sh`;
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<span className="text-sm text-muted-foreground mt-2">
|
||||||
|
You will only be able to see the
|
||||||
|
configuration once.
|
||||||
|
</span>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="terms"
|
id="terms"
|
||||||
|
|
|
@ -141,7 +141,6 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={sites}
|
data={sites}
|
||||||
addSite={() => {
|
addSite={() => {
|
||||||
// router.push(`/${orgId}/settings/sites/create`);
|
|
||||||
setIsCreateModalOpen(true);
|
setIsCreateModalOpen(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -88,7 +88,7 @@ export function AccountForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|
|
@ -55,7 +55,7 @@ export function AppearanceForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="font"
|
name="font"
|
||||||
|
|
|
@ -76,7 +76,7 @@ export function DisplayForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="items"
|
name="items"
|
||||||
|
|
|
@ -64,7 +64,7 @@ export default function SettingsLayout({ children }: SettingsLayoutProps) {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Separator className="my-6" />
|
<Separator className="my-6" />
|
||||||
<div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
|
<div className="flex flex-col space-y-4 lg:flex-row lg:space-x-12 lg:space-y-0">
|
||||||
<aside className="-mx-4 lg:w-1/5">
|
<aside className="-mx-4 lg:w-1/5">
|
||||||
<SidebarNav items={sidebarNavItems} />
|
<SidebarNav items={sidebarNavItems} />
|
||||||
</aside>
|
</aside>
|
||||||
|
|
|
@ -60,7 +60,7 @@ export function NotificationsForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="type"
|
name="type"
|
||||||
|
|
|
@ -88,7 +88,7 @@ export function ProfileForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="username"
|
name="username"
|
||||||
|
|
|
@ -37,6 +37,7 @@ export default function CopyTextBox({ text = "", wrapText = false }) {
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon"
|
size="icon"
|
||||||
|
type="button"
|
||||||
className="absolute top-1 right-1 z-10"
|
className="absolute top-1 right-1 z-10"
|
||||||
onClick={copyToClipboard}
|
onClick={copyToClipboard}
|
||||||
aria-label="Copy to clipboard"
|
aria-label="Copy to clipboard"
|
||||||
|
|
|
@ -88,7 +88,7 @@ export function AccountForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|
|
@ -62,7 +62,7 @@ export function AppearanceForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="font"
|
name="font"
|
||||||
|
|
|
@ -76,7 +76,7 @@ export function DisplayForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="items"
|
name="items"
|
||||||
|
|
|
@ -60,7 +60,7 @@ export function NotificationsForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="type"
|
name="type"
|
||||||
|
|
|
@ -88,7 +88,7 @@ export function ProfileForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="username"
|
name="username"
|
||||||
|
|
|
@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed left-[50%] top-[30%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue