Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): Add org settings frontend #718

Merged
merged 5 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/app/organization/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function OrganizationLayout({
{/* DynamicNavbar needs a WorkflowProvider and a WorkspaceProvider */}
<DynamicNavbar />
<div className="container h-full space-y-6 overflow-auto md:block">
<div className="flex h-full flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
<div className="flex h-full flex-col space-y-8 lg:flex-row lg:space-y-0">
<aside className="-mx-4 h-full lg:w-1/5">
<OrganizationSidebarNav />
</aside>
Expand Down
27 changes: 27 additions & 0 deletions frontend/src/app/organization/settings/auth/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client"

import { Separator } from "@/components/ui/separator"
import { OrgSettingsAuthForm } from "@/components/organization/org-settings-auth"

export default function AuthSettingsPage() {
return (
<div className="size-full overflow-auto">
<div className="container flex h-full max-w-[1000px] flex-col space-y-12">
<div className="space-y-8">
<div className="flex w-full">
<div className="items-start space-y-3 text-left">
<h2 className="text-2xl font-semibold tracking-tight">
Authentication
</h2>
<p className="text-md text-muted-foreground">
View and manage your organization authentication settings here.
</p>
</div>
</div>
<Separator />
</div>
<OrgSettingsAuthForm />
</div>
</div>
)
}
27 changes: 27 additions & 0 deletions frontend/src/app/organization/settings/git/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client"

import { Separator } from "@/components/ui/separator"
import { OrgSettingsGitForm } from "@/components/organization/org-settings-git"

export default function GitSettingsPage() {
return (
<div className="size-full overflow-auto">
<div className="container flex h-full max-w-[1000px] flex-col space-y-12">
<div className="space-y-8">
<div className="flex w-full">
<div className="items-start space-y-3 text-left">
<h2 className="text-2xl font-semibold tracking-tight">
Git Repository
</h2>
<p className="text-md text-muted-foreground">
View and manage your organization Git settings here.
</p>
</div>
</div>
<Separator />
</div>
<OrgSettingsGitForm />
</div>
</div>
)
}
27 changes: 27 additions & 0 deletions frontend/src/app/organization/settings/oauth/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client"

import { Separator } from "@/components/ui/separator"
import { OrgSettingsOAuthForm } from "@/components/organization/org-settings-oauth"

export default function OAuthSettingsPage() {
return (
<div className="size-full overflow-auto">
<div className="container flex h-full max-w-[1000px] flex-col space-y-12">
<div className="space-y-8">
<div className="flex w-full">
<div className="items-start space-y-3 text-left">
<h2 className="text-2xl font-semibold tracking-tight">
OAuth Settings
</h2>
<p className="text-md text-muted-foreground">
View and manage your organization OAuth settings here.
</p>
</div>
</div>
<Separator />
</div>
<OrgSettingsOAuthForm />
</div>
</div>
)
}
18 changes: 18 additions & 0 deletions frontend/src/app/organization/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use client"

export default function SettingsPage() {
return (
<div className="size-full overflow-auto">
<div className="container flex h-full max-w-[1000px] flex-col space-y-12">
<div className="flex w-full">
<div className="items-start space-y-3 text-left">
<h2 className="text-2xl font-semibold tracking-tight">Settings</h2>
<p className="text-md text-muted-foreground">
View and manage your organization settings here.
</p>
</div>
</div>
</div>
</div>
)
}
27 changes: 27 additions & 0 deletions frontend/src/app/organization/settings/sso/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client"

import { Separator } from "@/components/ui/separator"
import { OrgSettingsSsoForm } from "@/components/organization/org-settings-sso"

export default function SsoSettingsPage() {
return (
<div className="size-full overflow-auto">
<div className="container flex h-full max-w-[1000px] flex-col space-y-12">
<div className="space-y-8">
<div className="flex w-full">
<div className="items-start space-y-3 text-left">
<h2 className="text-2xl font-semibold tracking-tight">
SAML Single Sign-On
</h2>
<p className="text-md text-muted-foreground">
View and manage your organization SAML SSO settings here.
</p>
</div>
</div>
<Separator />
</div>
<OrgSettingsSsoForm />
</div>
</div>
)
}
33 changes: 27 additions & 6 deletions frontend/src/app/organization/sidebar-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import Link from "next/link"
import { usePathname } from "next/navigation"
import { KeyRoundIcon, LucideIcon, UsersIcon } from "lucide-react"
import { KeyRoundIcon, LucideIcon, SettingsIcon, UsersIcon } from "lucide-react"

import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
Expand All @@ -13,6 +13,25 @@ type NavItem = {
href: string
}

const settingsNavItems: NavItem[] = [
{
title: "Git Repository",
href: "/organization/settings/git",
},
{
title: "Single Sign-On",
href: "/organization/settings/sso",
},
{
title: "OAuth",
href: "/organization/settings/oauth",
},
{
title: "Authentication",
href: "/organization/settings/auth",
},
]

const secretNavItems: NavItem[] = [
{
title: "Credentials",
Expand Down Expand Up @@ -43,10 +62,15 @@ interface SidebarNavProps extends React.HTMLAttributes<HTMLElement> {

export function OrganizationSidebarNav() {
return (
<div className="h-full space-y-4 border-r pr-4 pt-16">
<div className="h-full space-y-4 pr-4 pt-16">
<div className="space-y-0.5">
<h2 className="text-xl font-bold tracking-tight">Organization</h2>
</div>
<SidebarNavBlock
title="Settings"
icon={SettingsIcon}
items={settingsNavItems}
/>
<SidebarNavBlock
title="Secrets"
icon={KeyRoundIcon}
Expand All @@ -56,7 +80,6 @@ export function OrganizationSidebarNav() {
</div>
)
}
const defaultRoute = "general"

export function SidebarNavBlock({
className,
Expand All @@ -66,7 +89,6 @@ export function SidebarNavBlock({
...props
}: SidebarNavProps) {
const pathname = usePathname()
const leafRoute = pathname.split("/").pop()

return (
<nav
Expand All @@ -86,8 +108,7 @@ export function SidebarNavBlock({
href={item.href}
className={cn(
buttonVariants({ variant: "ghost" }),
item.href.endsWith(leafRoute ?? defaultRoute) &&
"bg-muted-foreground/10",
pathname === item.href && "bg-muted-foreground/10",
"h-8 justify-start hover:cursor-default"
)}
>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/auth/saml.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"use client"

import { ComponentPropsWithoutRef, useState } from "react"
import { authSamlDatabaseLogin } from "@/client"

import { Button } from "@/components/ui/button"
import { Icons } from "@/components/icons"
import { authSamlDatabaseLogin } from "@/client"

type SamlSSOButtonProps = ComponentPropsWithoutRef<typeof Button>
export function SamlSSOButton(props: SamlSSOButtonProps) {
Expand Down
29 changes: 19 additions & 10 deletions frontend/src/components/auth/sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import TracecatIcon from "public/icon.png"
import { useForm } from "react-hook-form"
import { z } from "zod"

import { authConfig } from "@/config/auth"
import { useAppInfo } from "@/lib/hooks"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import {
Expand All @@ -33,14 +33,27 @@ import { toast } from "@/components/ui/use-toast"
import { GoogleOAuthButton } from "@/components/auth/oauth-buttons"
import { SamlSSOButton } from "@/components/auth/saml"
import { Icons } from "@/components/icons"
import { CenteredSpinner } from "@/components/loading/spinner"

export function SignIn({ className }: React.HTMLProps<HTMLDivElement>) {
const { user } = useAuth()
const { appInfo, appInfoIsLoading } = useAppInfo()
const router = useRouter()
if (user) {
router.push("/workspaces")
}

if (appInfoIsLoading) {
return <CenteredSpinner />
}

const allowedAuthTypes: string[] = appInfo?.auth_allowed_types ?? []
const showBasicAuth =
allowedAuthTypes.includes("basic") && appInfo?.auth_basic_enabled
const showGoogleOauthAuth =
allowedAuthTypes.includes("google_oauth") && appInfo?.oauth_google_enabled
const showSamlAuth =
allowedAuthTypes.includes("saml") && appInfo?.saml_enabled
return (
<div
className={cn(
Expand All @@ -57,8 +70,8 @@ export function SignIn({ className }: React.HTMLProps<HTMLDivElement>) {
</CardDescription>
</CardHeader>
<CardContent className="flex-col space-y-2">
{authConfig.authTypes.includes("basic") && <BasicLoginForm />}
{authConfig.authTypes.length > 1 && (
{showBasicAuth && <BasicLoginForm />}
{(showGoogleOauthAuth || showSamlAuth) && (
<div className="relative">
<div className="absolute inset-0 flex items-center">
<span className="w-full border-t" />
Expand All @@ -70,15 +83,11 @@ export function SignIn({ className }: React.HTMLProps<HTMLDivElement>) {
</div>
</div>
)}
{authConfig.authTypes.includes("google_oauth") && (
<GoogleOAuthButton className="w-full" />
)}
{authConfig.authTypes.includes("saml") && (
<SamlSSOButton className="w-full" />
)}
{showGoogleOauthAuth && <GoogleOAuthButton className="w-full" />}
{showSamlAuth && <SamlSSOButton className="w-full" />}
{/* <GithubOAuthButton disabled className="hover:cur" /> */}
</CardContent>
{authConfig.authTypes.includes("basic") && (
{showBasicAuth && (
<CardFooter className="flex items-center justify-center text-sm text-muted-foreground">
<div className="mt-4 text-center">
Don&apos;t have an account?{" "}
Expand Down
Loading
Loading