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

refactor(ui): simplify implementation of github provider using PAT #1963

Merged
merged 20 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
10 changes: 6 additions & 4 deletions ee/tabby-ui/app/(dashboard)/components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default function Sidebar({ children, className }: SidebarProps) {
</>
}
>
<SidebarButton href="/settings/git">
<SidebarButton href="/settings/git/gitops">
Git Providers
</SidebarButton>
<SidebarButton href="/settings/sso">SSO</SidebarButton>
Expand Down Expand Up @@ -135,9 +135,11 @@ const linkVariants = cva(
function SidebarButton({ href, children }: SidebarButtonProps) {
const pathname = usePathname()
const isSelected = React.useMemo(() => {
return href === '/'
? href === pathname
: shouldPathnameHighlight(pathname, href)
if (href === '/') return href === pathname
if (href === '/settings/git/gitops')
return pathname.startsWith('/settings/git/')

return shouldPathnameHighlight(pathname, href)
}, [pathname, href])

const state = isSelected ? 'selected' : 'not-selected'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client'

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

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

export default function GitTabsHeader() {
const pathname = usePathname()
const defualtValue = pathname.startsWith('/settings/git/generic')
? 'generic'
: 'gitops'

return (
<Tabs defaultValue={defualtValue}>
<div className="sticky top-0 mb-4 flex">
<TabsList className="grid grid-cols-2">
<TabsTrigger value="gitops" asChild>
<Link href="/settings/git/gitops">Gitops</Link>
</TabsTrigger>
<TabsTrigger value="generic" asChild>
<Link href="/settings/git/generic">Generic Git Repositories</Link>
</TabsTrigger>
</TabsList>
</div>
</Tabs>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
import Link from 'next/link'

import { buttonVariants } from '@/components/ui/button'
import { CardTitle } from '@/components/ui/card'

import { RepositoryHeader } from './header'
import RepositoryTable from './repository-table'

export default function Repository() {
return (
<>
<RepositoryHeader />
<CardTitle className="py-6">Generic git repositories</CardTitle>
<RepositoryTable />
<div className="mt-4 flex justify-end">
<Link href="/settings/git/new" className={buttonVariants()}>
<Link href="/settings/git/generic/new" className={buttonVariants()}>
Create
</Link>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react'

import { SubHeader } from '@/components/sub-header'

export const RepositoryHeader = ({ className }: { className?: string }) => {
const Header = ({ className }: { className?: string }) => {
return (
<SubHeader
className={className}
Expand All @@ -11,3 +13,16 @@ export const RepositoryHeader = ({ className }: { className?: string }) => {
</SubHeader>
)
}

export default function GenericGitRepositoriesLayout({
children
}: {
children: React.ReactNode
}) {
return (
<>
<Header />
{children}
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default function CreateRepositoryForm({
)}
/>
<div className="flex justify-end gap-4">
<Link href="/settings/git">
<Link href="/settings/git/generic">
<Button type="button" variant="ghost" disabled={isSubmitting}>
Cancel
</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client'

import { useRouter } from 'next/navigation'

import { Button } from '@/components/ui/button'
import { CardTitle } from '@/components/ui/card'
import { IconChevronLeft } from '@/components/ui/icons'

import RepositoryForm from './create-repository-form'

export const NewRepository = () => {
const router = useRouter()

const onCreated = () => {
router.replace('/settings/git/generic')
}

return (
<>
<CardTitle className="py-6">
<div className="-ml-1 flex items-center">
<Button
onClick={() => router.back()}
variant={'ghost'}
className="h-6 px-1"
>
<IconChevronLeft className="h-5 w-5" />
</Button>
<span className="ml-2">Create Generic git repository</span>
</div>
</CardTitle>
<RepositoryForm onCreated={onCreated} />
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Repository from './components/repository'

export default function Generic() {
return <Repository />
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { IconGitHub } from '@/components/ui/icons'
import LoadingWrapper from '@/components/loading-wrapper'

import { RepositoryHeader } from './header'

export default function GitProvidersPage() {
const [{ data, fetching }] = useQuery({
query: listGithubRepositoryProviders
Expand All @@ -20,13 +18,16 @@ export default function GitProvidersPage() {

return (
<>
<RepositoryHeader />
<CardTitle className="py-6">Git Providers</CardTitle>
<LoadingWrapper loading={fetching}>
{githubRepositoryProviders?.length ? (
<div>
<GitProvidersList data={githubRepositoryProviders} />
<div className="mt-4 flex justify-end">
<Link href="/settings/gitops/new" className={buttonVariants()}>
<Link
href="/settings/git/gitops/new"
className={buttonVariants()}
>
Create
</Link>
</div>
Expand Down Expand Up @@ -58,7 +59,7 @@ const GitProvidersList: React.FC<GitProvidersTableProps> = ({ data }) => {
</div>
</CardTitle>
<Link
href={`/settings/gitops/detail?id=${item.node.id}`}
href={`/settings/git/gitops/detail?id=${item.node.id}`}
className={buttonVariants({ variant: 'secondary' })}
>
View
Expand All @@ -70,12 +71,6 @@ const GitProvidersList: React.FC<GitProvidersTableProps> = ({ data }) => {
<span className="w-[30%] text-muted-foreground">Name</span>
<span>{item.node.displayName}</span>
</div>
<div className="flex border-b py-3">
<span className="w-[30%] shrink-0 text-muted-foreground">
Application ID
</span>
<span className="truncate">{item.node.applicationId}</span>
</div>
<div className="flex py-3">
<span className="w-[30%] shrink-0 text-muted-foreground">
Status
Expand All @@ -98,7 +93,7 @@ const GitProvidersPlaceholder = () => {
<div>No Data</div>
<div className="flex justify-center">
<Link
href="/settings/gitops/new"
href="/settings/git/gitops/new"
className={buttonVariants({ variant: 'default' })}
>
Create
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
'use client'

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

import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage
} from '@/components/ui/form'
import { IconGitHub } from '@/components/ui/icons'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'

export const createGitProviderSchema = z.object({
provider: z.string(),
displayName: z
.string()
.trim()
.regex(
/^[\w-]+$/,
'Display name must contain only alphanumeric characters, underscores, and hyphens'
),
accessToken: z.string()
})

export const updateGitProviderSchema = createGitProviderSchema.extend({
provider: createGitProviderSchema.shape.provider.optional()
})

export type CreateGitProviderFormValues = z.infer<
typeof createGitProviderSchema
>
export type UpdateGitProviderFormValues = z.infer<
typeof updateGitProviderSchema
>

interface GitProviderFormProps {
isNew?: boolean
defaultValues?: Partial<z.infer<typeof createGitProviderSchema>>
footer: React.ReactNode
onSubmit: (values: any) => Promise<any>
}

export const GitProviderForm = React.forwardRef<
{
form: UseFormReturn<
CreateGitProviderFormValues | UpdateGitProviderFormValues
>
},
GitProviderFormProps
>(({ isNew, defaultValues, footer, onSubmit }, ref) => {
const formSchema = isNew ? createGitProviderSchema : updateGitProviderSchema
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues
})

React.useImperativeHandle(
ref,
() => {
return {
form
}
},
[form]
)

return (
<Form {...form}>
<div className="grid gap-2">
<form className="grid gap-6" onSubmit={form.handleSubmit(onSubmit)}>
{isNew && (
<FormField
control={form.control}
name="provider"
disabled={!isNew}
render={({ field: { onChange, ...rest } }) => (
<FormItem>
<FormLabel required>Choose Git provider</FormLabel>
<FormControl>
<RadioGroup
className="flex flex-wrap gap-6"
orientation="horizontal"
onValueChange={onChange}
{...rest}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="github" id="github" />
<Label
className="flex cursor-pointer items-center gap-1"
htmlFor="github"
>
<IconGitHub className="h-5 w-5" />
<span>GitHub.com</span>
</Label>
</div>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
<FormField
control={form.control}
name="displayName"
render={({ field }) => (
<FormItem>
<FormLabel required>Display name</FormLabel>
<FormDescription>
A display name to help identifying among different configs
using the same Git provider.
</FormDescription>
<FormControl>
<Input
placeholder="e.g. GitHub"
autoCapitalize="none"
autoCorrect="off"
autoComplete="off"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="accessToken"
render={({ field }) => (
<FormItem>
<FormLabel required={isNew}>Access Token</FormLabel>
<FormDescription>
<div>
Create a dedicated service user and generate a fine-grained
personal access token with the member role for the
organization or all projects to be managed.
</div>
<div className="ml-2">• Contents (Read-only)</div>
</FormDescription>
<FormControl>
<Input
placeholder="e.g. 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0NB"
autoCapitalize="none"
autoCorrect="off"
autoComplete="off"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{footer}
{/* <div className="flex items-center justify-between">
<div>
<FormMessage />
</div>
<div>
<Button
type="submit"
disabled={isSubmitting || (!isNew && !isDirty)}
>
{isSubmitting && <IconSpinner className="mr-2" />}
{isNew ? 'Create' : 'Update'}
</Button>
</div>
</div> */}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rmove?

</form>
</div>
</Form>
)
})

GitProviderForm.displayName = 'GitProviderForm'
Loading
Loading