Skip to content

Commit

Permalink
refactor(ui): merge members page and user groups page (TabbyML#3149)
Browse files Browse the repository at this point in the history
* refactor(ui): merge members page and user groups page

* [autofix.ci] apply automated fixes

* Update ee/tabby-ui/app/(dashboard)/components/sidebar.tsx

Co-authored-by: Meng Zhang <[email protected]>

* Update ee/tabby-ui/app/(dashboard)/settings/team/components/team-nav.tsx

Co-authored-by: Meng Zhang <[email protected]>

* Update ee/tabby-ui/app/(dashboard)/settings/team/components/team.tsx

Co-authored-by: Meng Zhang <[email protected]>

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Meng Zhang <[email protected]>
  • Loading branch information
3 people authored Sep 18, 2024
1 parent 531062a commit f62fe40
Show file tree
Hide file tree
Showing 13 changed files with 1,251 additions and 19 deletions.
8 changes: 2 additions & 6 deletions ee/tabby-ui/app/(dashboard)/components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,8 @@ const menus: Menu[] = [
href: '/settings/general'
},
{
title: 'Members',
href: '/settings/team'
},
{
title: 'User Groups',
href: '/settings/user-group',
title: 'Users & Groups',
href: '/settings/team',
allowUser: true
},
{
Expand Down
24 changes: 24 additions & 0 deletions ee/tabby-ui/app/(dashboard)/settings/team/components/team-nav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use client'

import Link from 'next/link'
import { usePathname } from 'next/navigation'

import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'

export function TeamNav() {
const pathname = usePathname()
const isGroupsPath = pathname.includes('/team/groups')

return (
<Tabs value={isGroupsPath ? 'groups' : 'members'} className="mb-8">
<TabsList>
<Link href="/settings/team">
<TabsTrigger value="members">Users</TabsTrigger>
</Link>
<Link href="/settings/team/groups">
<TabsTrigger value="groups">Groups</TabsTrigger>
</Link>
</TabsList>
</Tabs>
)
}
31 changes: 19 additions & 12 deletions ee/tabby-ui/app/(dashboard)/settings/team/components/team.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
'use client'

import { useMe } from '@/lib/hooks/use-me'
import { CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import LoadingWrapper from '@/components/loading-wrapper'

import InvitationTable from './invitation-table'
import UsersTable from './user-table'

export default function Team() {
const [{ data, fetching }] = useMe()
return (
<>
<LoadingWrapper loading={fetching}>
{data?.me.isAdmin && (
<>
<div>
<CardHeader className="pl-0 pt-0">
<CardTitle>Pending Invites</CardTitle>
</CardHeader>
<CardContent className="pl-0">
<InvitationTable />
</CardContent>
</div>
<div className="h-16" />
</>
)}
<div>
<CardHeader className="pl-0 pt-0">
<CardTitle>Pending Invites</CardTitle>
</CardHeader>
<CardContent className="pl-0">
<InvitationTable />
</CardContent>
</div>
<div className="h-16" />
<div>
<CardHeader className="pl-0 pt-0">
<CardTitle>Members</CardTitle>
<CardTitle>Users</CardTitle>
</CardHeader>
<CardContent className="pl-0">
<UsersTable />
</CardContent>
</div>
</>
</LoadingWrapper>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export default function UsersTable() {
<TableBody>
{users.edges.map(x => {
const showOperation =
!x.node.isOwner && me?.me && x.node.id !== me.me.id
!x.node.isOwner && me?.me?.isAdmin && x.node.id !== me.me.id

return (
<TableRow key={x.node.id}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
'use client'

import * as React from 'react'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import * as z from 'zod'

import { graphql } from '@/lib/gql/generates'
import { useMutation } from '@/lib/tabby/gql'
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@/components/ui/dialog'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage
} from '@/components/ui/form'
import { IconSpinner } from '@/components/ui/icons'
import { Input } from '@/components/ui/input'

const createUserGroupMutation = graphql(/* GraphQL */ `
mutation createUserGroup($input: CreateUserGroupInput!) {
createUserGroup(input: $input)
}
`)

const formSchema = z.object({
name: z.string().trim()
})

export default function CreateUserGroupDialog({
onSubmit,
children
}: {
onSubmit: (userId: string) => void
children: React.ReactNode
}) {
const [open, setOpen] = React.useState(false)
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema)
})

const { isSubmitting } = form.formState

const onOpenChange = (open: boolean) => {
if (isSubmitting) {
return
}

// reset form
if (!open) {
setTimeout(() => {
form.reset()
}, 500)
}
setOpen(open)
}

const createUserGroup = useMutation(createUserGroupMutation, {
form
})

const handleSubmit = async (values: z.infer<typeof formSchema>) => {
return createUserGroup({
input: {
name: values.name
}
})
.then(res => {
if (!res?.data?.createUserGroup) {
return
}

onSubmit?.(res.data.createUserGroup)
onOpenChange(false)
})
.catch(() => {})
}

return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader className="gap-3">
<DialogTitle>Create User Group</DialogTitle>
</DialogHeader>
<Form {...form}>
<div className="grid gap-2">
<form
className="grid gap-6"
onSubmit={form.handleSubmit(handleSubmit)}
>
<div className="grid gap-2">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel required>Name</FormLabel>
<FormDescription>
The name should be unique, and it cannot be changed
after creation.
</FormDescription>
<FormControl>
<Input
placeholder="User Group Name"
autoCapitalize="none"
autoCorrect="off"
autoComplete="off"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* root error message */}
<FormMessage />
</div>
<div className="flex justify-end gap-4">
<Button
type="button"
variant="ghost"
disabled={isSubmitting}
onClick={() => onOpenChange(false)}
>
Cancel
</Button>
<Button type="submit" disabled={isSubmitting}>
{isSubmitting && <IconSpinner className="mr-2" />}
Create
</Button>
</div>
</form>
</div>
</Form>
</DialogContent>
<DialogTrigger asChild>{children}</DialogTrigger>
</Dialog>
)
}
Loading

0 comments on commit f62fe40

Please sign in to comment.