diff --git a/src/app/[orgId]/settings/general/layout.tsx b/src/app/[orgId]/settings/general/layout.tsx new file mode 100644 index 00000000..f5ea8c72 --- /dev/null +++ b/src/app/[orgId]/settings/general/layout.tsx @@ -0,0 +1,85 @@ +import { internal } from "@app/api"; +import { authCookieHeader } from "@app/api/cookies"; +import { SidebarSettings } from "@app/components/SidebarSettings"; +import { verifySession } from "@app/lib/auth/verifySession"; +import OrgProvider from "@app/providers/OrgProvider"; +import OrgUserProvider from "@app/providers/OrgUserProvider"; +import { GetOrgResponse } from "@server/routers/org"; +import { GetOrgUserResponse } from "@server/routers/user"; +import { AxiosResponse } from "axios"; +import { redirect } from "next/navigation"; +import { cache } from "react"; + +type GeneralSettingsProps = { + children: React.ReactNode; + params: Promise<{ orgId: string }>; +}; + +export default async function GeneralSettingsPage({ + children, + params, +}: GeneralSettingsProps) { + const { orgId } = await params; + + const getUser = cache(verifySession); + const user = await getUser(); + + if (!user) { + redirect("/auth/login"); + } + + let orgUser = null; + try { + const getOrgUser = cache(async () => + internal.get>( + `/org/${orgId}/user/${user.userId}`, + await authCookieHeader() + ) + ); + const res = await getOrgUser(); + orgUser = res.data.data; + } catch { + redirect(`/${orgId}`); + } + + let org = null; + try { + const getOrg = cache(async () => + internal.get>( + `/org/${orgId}`, + await authCookieHeader() + ) + ); + const res = await getOrg(); + org = res.data.data; + } catch { + redirect(`/${orgId}`); + } + + const sidebarNavItems = [ + { + title: "General", + href: `/{orgId}/settings/general`, + }, + ]; + + return ( + <> + + +
+

+ General +

+

+ Configure your organization's general settings +

+
+ + {children} + +
+
+ + ); +} diff --git a/src/app/[orgId]/settings/general/page.tsx b/src/app/[orgId]/settings/general/page.tsx new file mode 100644 index 00000000..c215337c --- /dev/null +++ b/src/app/[orgId]/settings/general/page.tsx @@ -0,0 +1,58 @@ +"use client"; + +import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; +import { Button } from "@app/components/ui/button"; +import { useOrgContext } from "@app/hooks/useOrgContext"; +import { userOrgUserContext } from "@app/hooks/useOrgUserContext"; +import { useState } from "react"; + +export default function GeneralPage() { + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + + const { orgUser } = userOrgUserContext(); + const { org } = useOrgContext(); + + async function deleteOrg() { + console.log("not implemented"); + } + + return ( + <> + { + setIsDeleteModalOpen(val); + }} + dialog={ +
+

+ Are you sure you want to delete the organization{" "} + {org?.org.name}? +

+ +

+ This action is irreversible and will delete all + associated data. +

+ +

+ To confirm, type the name of the organization below. +

+
+ } + buttonText="Confirm delete organization" + onConfirm={deleteOrg} + string={org?.org.name || ""} + title="Delete organization" + /> + + {orgUser.isOwner ? ( + + ) : ( +

Nothing to see here

+ )} + + ); +} diff --git a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx index 4814e34b..a745d52a 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx @@ -96,99 +96,113 @@ export default function GeneralForm() { } return ( -
- - ( - - Name - - - - - This is the display name of the resource. - - - - )} - /> - ( - - Site - - - - - - - - - - - - No site found. - - - {sites.map((site) => ( - { - form.setValue( - "siteId", - site.siteId - ); - }} - > - - {site.name} - - ))} - - - - - - - This is the site that will be used in the - dashboard. - - - - )} - /> - - - + <> +
+

+ General Settings +

+

+ Configure the general settings for this resource +

+
+ +
+ + ( + + Name + + + + + This is the display name of the resource. + + + + )} + /> + ( + + Site + + + + + + + + + + + + No site found. + + + {sites.map((site) => ( + { + form.setValue( + "siteId", + site.siteId + ); + }} + > + + {site.name} + + ))} + + + + + + + This is the site that will be used in the + dashboard. + + + + )} + /> + + + + ); } diff --git a/src/app/[orgId]/settings/resources/[resourceId]/targets/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/targets/page.tsx index 9a108941..ed763bf5 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/targets/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/targets/page.tsx @@ -25,11 +25,9 @@ const isValidIPAddress = (ip: string) => { return ipv4Regex.test(ip); }; -export default function ReverseProxyTargets( - props: { - params: Promise<{ resourceId: number }>; - } -) { +export default function ReverseProxyTargets(props: { + params: Promise<{ resourceId: number }>; +}) { const params = use(props.params); const [targets, setTargets] = useState([]); const [nextId, setNextId] = useState(1); @@ -39,7 +37,7 @@ export default function ReverseProxyTargets( if (typeof window !== "undefined") { const fetchSites = async () => { const res = await api.get>( - `/resource/${params.resourceId}/targets`, + `/resource/${params.resourceId}/targets` ); setTargets(res.data.data.targets); }; @@ -93,7 +91,7 @@ export default function ReverseProxyTargets( }) .then((res) => { setTargets( - targets.filter((target) => target.targetId !== targetId), + targets.filter((target) => target.targetId !== targetId) ); }); }; @@ -103,8 +101,8 @@ export default function ReverseProxyTargets( targets.map((target) => target.targetId === targetId ? { ...target, enabled: !target.enabled } - : target, - ), + : target + ) ); api.post(`/target/${targetId}`, { enabled: !targets.find((target) => target.targetId === targetId) @@ -115,7 +113,14 @@ export default function ReverseProxyTargets( }; return ( -
+
+
+

Targets

+

+ Setup the targets for the reverse proxy +

+
+
{ e.preventDefault(); @@ -192,9 +197,7 @@ export default function ReverseProxyTargets(
- +
diff --git a/src/app/[orgId]/settings/resources/components/ResourcesTable.tsx b/src/app/[orgId]/settings/resources/components/ResourcesTable.tsx index f07d1e54..7e431f20 100644 --- a/src/app/[orgId]/settings/resources/components/ResourcesTable.tsx +++ b/src/app/[orgId]/settings/resources/components/ResourcesTable.tsx @@ -15,6 +15,8 @@ import { useRouter } from "next/navigation"; import api from "@app/api"; import CreateResourceForm from "./CreateResourceForm"; import { useState } from "react"; +import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; +import { set } from "zod"; export type ResourceRow = { id: number; @@ -33,6 +35,20 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { const router = useRouter(); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [selectedResource, setSelectedResource] = + useState(); + + const deleteResource = (resourceId: number) => { + api.delete(`/resource/${resourceId}`) + .catch((e) => { + console.error("Error deleting resource", e); + }) + .then(() => { + router.refresh(); + setIsDeleteModalOpen(false); + }); + }; const columns: ColumnDef[] = [ { @@ -78,16 +94,6 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { const resourceRow = row.original; - const deleteResource = (resourceId: number) => { - api.delete(`/resource/${resourceId}`) - .catch((e) => { - console.error("Error deleting resource", e); - }) - .then(() => { - router.refresh(); - }); - }; - return ( @@ -106,9 +112,10 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) {